Skip to content

Commit

Permalink
Add initial implementation of BindByIndex
Browse files Browse the repository at this point in the history
  • Loading branch information
atifaziz committed Jan 4, 2025
1 parent 9f6dc48 commit 6024d5d
Show file tree
Hide file tree
Showing 7 changed files with 297 additions and 0 deletions.
218 changes: 218 additions & 0 deletions MoreLinq/BindByIndex.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
#region License and Terms
// MoreLINQ - Extensions to LINQ to Objects
// Copyright (c) 2018 Atif Aziz. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion

namespace MoreLinq
{
using System;
using System.Collections;
using System.Collections.Generic;

static partial class MoreEnumerable
{
/// <summary>
/// TODO Complete documentation
/// </summary>
/// <typeparam name="T">
/// Type of elements in <paramref name="source"/> sequence.</typeparam>
/// <typeparam name="TResult">Type of result elements returned.</typeparam>
/// <param name="source">The source sequence.</param>
/// <param name="indices">The sequence of indices.</param>
/// <param name="missingSelector">
/// TODO Complete documentation
/// </param>
/// <param name="resultSelector">
/// TODO Complete documentation
/// </param>
/// <returns>
/// TODO Complete documentation
/// </returns>

public static IEnumerable<TResult>
BindByIndex<T, TResult>(this IEnumerable<T> source, IEnumerable<int> indices,
Func<int, TResult> missingSelector, Func<T, int, TResult> resultSelector) =>
BindByIndex(source, indices, null, missingSelector, resultSelector);

/// <summary>
/// TODO Complete documentation
/// </summary>
/// <typeparam name="T">
/// Type of elements in <paramref name="source"/> sequence.</typeparam>
/// <typeparam name="TResult">Type of result elements returned.</typeparam>
/// <param name="source">The source sequence.</param>
/// <param name="indices">The sequence of indices.</param>
/// <param name="lookBackSize">Size of look-back buffer.</param>
/// <param name="missingSelector">
/// TODO Complete documentation
/// </param>
/// <param name="resultSelector">
/// TODO Complete documentation
/// </param>
/// <returns>
/// TODO Complete documentation
/// </returns>

public static IEnumerable<TResult>
BindByIndex<T, TResult>(this IEnumerable<T> source, IEnumerable<int> indices,
int lookBackSize,
Func<int, TResult> missingSelector,
Func<T, int, TResult> resultSelector) =>
BindByIndex(source, indices, (int?)lookBackSize, missingSelector, resultSelector);

static IEnumerable<TResult>
BindByIndex<T, TResult>(IEnumerable<T> source, IEnumerable<int> indices,
int? lookBackSize,
Func<int, TResult> missingSelector,
Func<T, int, TResult> resultSelector)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (indices == null) throw new ArgumentNullException(nameof(indices));
if (lookBackSize < 0) throw new ArgumentOutOfRangeException(nameof(lookBackSize));
if (missingSelector == null) throw new ArgumentNullException(nameof(missingSelector));
if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector));

// TODO A version optimized for lists

return _(lookBackSize switch
{
{ } lbs and > 0 => new Queue<T>(lbs, lbs),
0 => null,
_ => new Queue<T>()
});

Check warning on line 94 in MoreLinq/BindByIndex.cs

View check run for this annotation

Codecov / codecov/patch

MoreLinq/BindByIndex.cs#L90-L94

Added lines #L90 - L94 were not covered by tests

IEnumerable<TResult> _(Queue<T>? queue)
{
using var rie = indices.GetEnumerator();
if (!rie.MoveNext())
yield break;

Check warning on line 100 in MoreLinq/BindByIndex.cs

View check run for this annotation

Codecov / codecov/patch

MoreLinq/BindByIndex.cs#L100

Added line #L100 was not covered by tests

while (rie.Current < 0)
{
yield return missingSelector(rie.Current);

Check warning on line 104 in MoreLinq/BindByIndex.cs

View check run for this annotation

Codecov / codecov/patch

MoreLinq/BindByIndex.cs#L103-L104

Added lines #L103 - L104 were not covered by tests
if (!rie.MoveNext())
yield break;
}

Check warning on line 107 in MoreLinq/BindByIndex.cs

View check run for this annotation

Codecov / codecov/patch

MoreLinq/BindByIndex.cs#L106-L107

Added lines #L106 - L107 were not covered by tests

var ri = rie.Current;
var si = 0;

Check warning on line 110 in MoreLinq/BindByIndex.cs

View check run for this annotation

Codecov / codecov/patch

MoreLinq/BindByIndex.cs#L109-L110

Added lines #L109 - L110 were not covered by tests

foreach (var item in source)
{

Check warning on line 113 in MoreLinq/BindByIndex.cs

View check run for this annotation

Codecov / codecov/patch

MoreLinq/BindByIndex.cs#L113

Added line #L113 was not covered by tests
while (si == ri)
{
yield return resultSelector(item, si);

Check warning on line 116 in MoreLinq/BindByIndex.cs

View check run for this annotation

Codecov / codecov/patch

MoreLinq/BindByIndex.cs#L115-L116

Added lines #L115 - L116 were not covered by tests
do
{

Check warning on line 118 in MoreLinq/BindByIndex.cs

View check run for this annotation

Codecov / codecov/patch

MoreLinq/BindByIndex.cs#L118

Added line #L118 was not covered by tests
if (!rie.MoveNext())
yield break;
ri = rie.Current;

Check warning on line 121 in MoreLinq/BindByIndex.cs

View check run for this annotation

Codecov / codecov/patch

MoreLinq/BindByIndex.cs#L120-L121

Added lines #L120 - L121 were not covered by tests
if (ri < si)
{

Check warning on line 123 in MoreLinq/BindByIndex.cs

View check run for this annotation

Codecov / codecov/patch

MoreLinq/BindByIndex.cs#L123

Added line #L123 was not covered by tests
if (queue is { } q && si - q.Count is var qi && ri >= qi)
yield return resultSelector(q[ri - qi], ri);

Check warning on line 125 in MoreLinq/BindByIndex.cs

View check run for this annotation

Codecov / codecov/patch

MoreLinq/BindByIndex.cs#L125

Added line #L125 was not covered by tests
else
yield return missingSelector(ri);
}
}

Check warning on line 129 in MoreLinq/BindByIndex.cs

View check run for this annotation

Codecov / codecov/patch

MoreLinq/BindByIndex.cs#L127-L129

Added lines #L127 - L129 were not covered by tests
while (ri < si);
}

Check warning on line 131 in MoreLinq/BindByIndex.cs

View check run for this annotation

Codecov / codecov/patch

MoreLinq/BindByIndex.cs#L131

Added line #L131 was not covered by tests

queue?.Enqueue(item);
si++;
}

Check warning on line 135 in MoreLinq/BindByIndex.cs

View check run for this annotation

Codecov / codecov/patch

MoreLinq/BindByIndex.cs#L134-L135

Added lines #L134 - L135 were not covered by tests

if (ri != si)
{
yield return missingSelector(ri);

Check warning on line 139 in MoreLinq/BindByIndex.cs

View check run for this annotation

Codecov / codecov/patch

MoreLinq/BindByIndex.cs#L138-L139

Added lines #L138 - L139 were not covered by tests
while (rie.MoveNext())
yield return missingSelector(rie.Current);
}
}
}

Check warning on line 144 in MoreLinq/BindByIndex.cs

View check run for this annotation

Codecov / codecov/patch

MoreLinq/BindByIndex.cs#L141-L144

Added lines #L141 - L144 were not covered by tests

/// <summary>
/// A queue implementation similar to
/// <see cref="System.Collections.Generic.Queue{T}"/> but which
/// supports a maximum count (exceeding which will cause an item to be
/// dequeued each to make space for a new one being queued) as well as
/// directly indexing into the queue to retrieve any one item.
/// </summary>

sealed class Queue<T>(int maxCount = 0, int capacity = 0) : IReadOnlyList<T>
{
T[] items = capacity > 0 ? new T[capacity] : [];
int firstIndex;
readonly int maxCount = maxCount;

int Capacity => this.items.Length;
public int Count { get; private set; }

T IReadOnlyList<T>.this[int index] => this[index];

Check warning on line 163 in MoreLinq/BindByIndex.cs

View check run for this annotation

Codecov / codecov/patch

MoreLinq/BindByIndex.cs#L163

Added line #L163 was not covered by tests

public T this[int index]
{
get
{
if (index < 0 || index >= Count)
{

Check warning on line 170 in MoreLinq/BindByIndex.cs

View check run for this annotation

Codecov / codecov/patch

MoreLinq/BindByIndex.cs#L170

Added line #L170 was not covered by tests
#pragma warning disable CA2201 // Do not raise reserved exception types
throw new IndexOutOfRangeException();

Check warning on line 172 in MoreLinq/BindByIndex.cs

View check run for this annotation

Codecov / codecov/patch

MoreLinq/BindByIndex.cs#L172

Added line #L172 was not covered by tests
#pragma warning restore CA2201
}

return Cell(index);
}
}

ref T Cell(int index) => ref this.items[(this.firstIndex + index) % Capacity];

public void Enqueue(T item)
{
if (this.maxCount > 0 && Count == this.maxCount)
_ = Dequeue();

Check warning on line 185 in MoreLinq/BindByIndex.cs

View check run for this annotation

Codecov / codecov/patch

MoreLinq/BindByIndex.cs#L185

Added line #L185 was not covered by tests

if (Count == Capacity)
{
var array = new T[Math.Max(4, Capacity * 2)];
for (var i = 0; i < Count; i++)
array[i] = this[i];
this.firstIndex = 0;
this.items = array;
}

Cell(Count++) = item;
}

public T Dequeue()
{
if (Count == 0)
throw new InvalidOperationException();

Check warning on line 202 in MoreLinq/BindByIndex.cs

View check run for this annotation

Codecov / codecov/patch

MoreLinq/BindByIndex.cs#L202

Added line #L202 was not covered by tests
var result = this[0];
this.firstIndex++;
--Count;
return result;
}

IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

Check warning on line 209 in MoreLinq/BindByIndex.cs

View check run for this annotation

Codecov / codecov/patch

MoreLinq/BindByIndex.cs#L209

Added line #L209 was not covered by tests

public IEnumerator<T> GetEnumerator()
{
for (var i = 0; i < Count; i++)
yield return this[i];
}
}
}
}
54 changes: 54 additions & 0 deletions MoreLinq/Extensions.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -716,6 +716,60 @@ public static IEnumerable<TResult> Batch<TSource, TResult>(this IEnumerable<TSou

}

/// <summary><c>BindByIndex</c> extension.</summary>

[GeneratedCode("MoreLinq.ExtensionsGenerator", "1.0.0.0")]
public static partial class BindByIndexExtension
{
/// <summary>
/// TODO Complete documentation
/// </summary>
/// <typeparam name="T">
/// Type of elements in <paramref name="source"/> sequence.</typeparam>
/// <typeparam name="TResult">Type of result elements returned.</typeparam>
/// <param name="source">The source sequence.</param>
/// <param name="indices">The sequence of indices.</param>
/// <param name="missingSelector">
/// TODO Complete documentation
/// </param>
/// <param name="resultSelector">
/// TODO Complete documentation
/// </param>
/// <returns>
/// TODO Complete documentation
/// </returns>

public static IEnumerable<TResult>
BindByIndex<T, TResult>(this IEnumerable<T> source, IEnumerable<int> indices,
Func<int, TResult> missingSelector, Func<T, int, TResult> resultSelector) => MoreEnumerable. BindByIndex(source, indices, missingSelector, resultSelector);

/// <summary>
/// TODO Complete documentation
/// </summary>
/// <typeparam name="T">
/// Type of elements in <paramref name="source"/> sequence.</typeparam>
/// <typeparam name="TResult">Type of result elements returned.</typeparam>
/// <param name="source">The source sequence.</param>
/// <param name="indices">The sequence of indices.</param>
/// <param name="lookBackSize">Size of look-back buffer.</param>
/// <param name="missingSelector">
/// TODO Complete documentation
/// </param>
/// <param name="resultSelector">
/// TODO Complete documentation
/// </param>
/// <returns>
/// TODO Complete documentation
/// </returns>

public static IEnumerable<TResult>
BindByIndex<T, TResult>(this IEnumerable<T> source, IEnumerable<int> indices,
int lookBackSize,
Func<int, TResult> missingSelector,
Func<T, int, TResult> resultSelector) => MoreEnumerable. BindByIndex(source, indices, lookBackSize, missingSelector, resultSelector);

}

/// <summary><c>Cartesian</c> extension.</summary>

[GeneratedCode("MoreLinq.ExtensionsGenerator", "1.0.0.0")]
Expand Down
5 changes: 5 additions & 0 deletions MoreLinq/PublicAPI/net6.0/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
#nullable enable
MoreLinq.Extensions.BindByIndexExtension
static MoreLinq.Extensions.BindByIndexExtension.BindByIndex<T, TResult>(this System.Collections.Generic.IEnumerable<T>! source, System.Collections.Generic.IEnumerable<int>! indices, System.Func<int, TResult>! missingSelector, System.Func<T, int, TResult>! resultSelector) -> System.Collections.Generic.IEnumerable<TResult>!
static MoreLinq.Extensions.BindByIndexExtension.BindByIndex<T, TResult>(this System.Collections.Generic.IEnumerable<T>! source, System.Collections.Generic.IEnumerable<int>! indices, int lookBackSize, System.Func<int, TResult>! missingSelector, System.Func<T, int, TResult>! resultSelector) -> System.Collections.Generic.IEnumerable<TResult>!
static MoreLinq.MoreEnumerable.BindByIndex<T, TResult>(this System.Collections.Generic.IEnumerable<T>! source, System.Collections.Generic.IEnumerable<int>! indices, System.Func<int, TResult>! missingSelector, System.Func<T, int, TResult>! resultSelector) -> System.Collections.Generic.IEnumerable<TResult>!
static MoreLinq.MoreEnumerable.BindByIndex<T, TResult>(this System.Collections.Generic.IEnumerable<T>! source, System.Collections.Generic.IEnumerable<int>! indices, int lookBackSize, System.Func<int, TResult>! missingSelector, System.Func<T, int, TResult>! resultSelector) -> System.Collections.Generic.IEnumerable<TResult>!
5 changes: 5 additions & 0 deletions MoreLinq/PublicAPI/net8.0/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
#nullable enable
MoreLinq.Extensions.BindByIndexExtension
static MoreLinq.Extensions.BindByIndexExtension.BindByIndex<T, TResult>(this System.Collections.Generic.IEnumerable<T>! source, System.Collections.Generic.IEnumerable<int>! indices, System.Func<int, TResult>! missingSelector, System.Func<T, int, TResult>! resultSelector) -> System.Collections.Generic.IEnumerable<TResult>!
static MoreLinq.Extensions.BindByIndexExtension.BindByIndex<T, TResult>(this System.Collections.Generic.IEnumerable<T>! source, System.Collections.Generic.IEnumerable<int>! indices, int lookBackSize, System.Func<int, TResult>! missingSelector, System.Func<T, int, TResult>! resultSelector) -> System.Collections.Generic.IEnumerable<TResult>!
static MoreLinq.MoreEnumerable.BindByIndex<T, TResult>(this System.Collections.Generic.IEnumerable<T>! source, System.Collections.Generic.IEnumerable<int>! indices, System.Func<int, TResult>! missingSelector, System.Func<T, int, TResult>! resultSelector) -> System.Collections.Generic.IEnumerable<TResult>!
static MoreLinq.MoreEnumerable.BindByIndex<T, TResult>(this System.Collections.Generic.IEnumerable<T>! source, System.Collections.Generic.IEnumerable<int>! indices, int lookBackSize, System.Func<int, TResult>! missingSelector, System.Func<T, int, TResult>! resultSelector) -> System.Collections.Generic.IEnumerable<TResult>!
5 changes: 5 additions & 0 deletions MoreLinq/PublicAPI/net9.0/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
#nullable enable
MoreLinq.Extensions.BindByIndexExtension
static MoreLinq.Extensions.BindByIndexExtension.BindByIndex<T, TResult>(this System.Collections.Generic.IEnumerable<T>! source, System.Collections.Generic.IEnumerable<int>! indices, System.Func<int, TResult>! missingSelector, System.Func<T, int, TResult>! resultSelector) -> System.Collections.Generic.IEnumerable<TResult>!
static MoreLinq.Extensions.BindByIndexExtension.BindByIndex<T, TResult>(this System.Collections.Generic.IEnumerable<T>! source, System.Collections.Generic.IEnumerable<int>! indices, int lookBackSize, System.Func<int, TResult>! missingSelector, System.Func<T, int, TResult>! resultSelector) -> System.Collections.Generic.IEnumerable<TResult>!
static MoreLinq.MoreEnumerable.BindByIndex<T, TResult>(this System.Collections.Generic.IEnumerable<T>! source, System.Collections.Generic.IEnumerable<int>! indices, System.Func<int, TResult>! missingSelector, System.Func<T, int, TResult>! resultSelector) -> System.Collections.Generic.IEnumerable<TResult>!
static MoreLinq.MoreEnumerable.BindByIndex<T, TResult>(this System.Collections.Generic.IEnumerable<T>! source, System.Collections.Generic.IEnumerable<int>! indices, int lookBackSize, System.Func<int, TResult>! missingSelector, System.Func<T, int, TResult>! resultSelector) -> System.Collections.Generic.IEnumerable<TResult>!
5 changes: 5 additions & 0 deletions MoreLinq/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
#nullable enable
MoreLinq.Extensions.BindByIndexExtension
static MoreLinq.Extensions.BindByIndexExtension.BindByIndex<T, TResult>(this System.Collections.Generic.IEnumerable<T>! source, System.Collections.Generic.IEnumerable<int>! indices, System.Func<int, TResult>! missingSelector, System.Func<T, int, TResult>! resultSelector) -> System.Collections.Generic.IEnumerable<TResult>!
static MoreLinq.Extensions.BindByIndexExtension.BindByIndex<T, TResult>(this System.Collections.Generic.IEnumerable<T>! source, System.Collections.Generic.IEnumerable<int>! indices, int lookBackSize, System.Func<int, TResult>! missingSelector, System.Func<T, int, TResult>! resultSelector) -> System.Collections.Generic.IEnumerable<TResult>!
static MoreLinq.MoreEnumerable.BindByIndex<T, TResult>(this System.Collections.Generic.IEnumerable<T>! source, System.Collections.Generic.IEnumerable<int>! indices, System.Func<int, TResult>! missingSelector, System.Func<T, int, TResult>! resultSelector) -> System.Collections.Generic.IEnumerable<TResult>!
static MoreLinq.MoreEnumerable.BindByIndex<T, TResult>(this System.Collections.Generic.IEnumerable<T>! source, System.Collections.Generic.IEnumerable<int>! indices, int lookBackSize, System.Func<int, TResult>! missingSelector, System.Func<T, int, TResult>! resultSelector) -> System.Collections.Generic.IEnumerable<TResult>!
5 changes: 5 additions & 0 deletions MoreLinq/PublicAPI/netstandard2.1/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
#nullable enable
MoreLinq.Extensions.BindByIndexExtension
static MoreLinq.Extensions.BindByIndexExtension.BindByIndex<T, TResult>(this System.Collections.Generic.IEnumerable<T>! source, System.Collections.Generic.IEnumerable<int>! indices, System.Func<int, TResult>! missingSelector, System.Func<T, int, TResult>! resultSelector) -> System.Collections.Generic.IEnumerable<TResult>!
static MoreLinq.Extensions.BindByIndexExtension.BindByIndex<T, TResult>(this System.Collections.Generic.IEnumerable<T>! source, System.Collections.Generic.IEnumerable<int>! indices, int lookBackSize, System.Func<int, TResult>! missingSelector, System.Func<T, int, TResult>! resultSelector) -> System.Collections.Generic.IEnumerable<TResult>!
static MoreLinq.MoreEnumerable.BindByIndex<T, TResult>(this System.Collections.Generic.IEnumerable<T>! source, System.Collections.Generic.IEnumerable<int>! indices, System.Func<int, TResult>! missingSelector, System.Func<T, int, TResult>! resultSelector) -> System.Collections.Generic.IEnumerable<TResult>!
static MoreLinq.MoreEnumerable.BindByIndex<T, TResult>(this System.Collections.Generic.IEnumerable<T>! source, System.Collections.Generic.IEnumerable<int>! indices, int lookBackSize, System.Func<int, TResult>! missingSelector, System.Func<T, int, TResult>! resultSelector) -> System.Collections.Generic.IEnumerable<TResult>!

0 comments on commit 6024d5d

Please sign in to comment.