Skip to content

Commit

Permalink
Added support for passing Range as a parameter to .Exclude. (#619)
Browse files Browse the repository at this point in the history
Co-authored-by: Taco <65432314+tradinglounge@users.noreply.github.com>
Co-authored-by: Stuart Turner <stuart@turner-isageek.com>
  • Loading branch information
3 people authored Feb 22, 2024
1 parent 886ca78 commit 635691e
Show file tree
Hide file tree
Showing 7 changed files with 220 additions and 18 deletions.
164 changes: 147 additions & 17 deletions Source/SuperLinq/Exclude.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace SuperLinq;
namespace SuperLinq;

public static partial class SuperEnumerable
{
Expand Down Expand Up @@ -35,43 +35,98 @@ public static IEnumerable<T> Exclude<T>(this IEnumerable<T> sequence, int startI
ArgumentOutOfRangeException.ThrowIfNegative(startIndex);
ArgumentOutOfRangeException.ThrowIfNegative(count);

if (count == 0)
return sequence;

return sequence switch
return (count, sequence) switch
{
IList<T> list => new ExcludeListIterator<T>(list, startIndex, count),
ICollection<T> collection => new ExcludeCollectionIterator<T>(collection, startIndex, count),
(0, _) => sequence,
(_, IList<T> list) => new ExcludeListIterator<T>(list, startIndex, count),
(_, ICollection<T> collection) => new ExcludeCollectionIterator<T>(collection, startIndex, count),
_ => ExcludeCore(sequence, startIndex, count)
};
}

private sealed class ExcludeCollectionIterator<T>(
ICollection<T> source,
int startIndex,
int count
) : CollectionIterator<T>
/// <summary>
/// Excludes a contiguous number of elements from a sequence starting at a given index.
/// </summary>
/// <typeparam name="T">
/// The type of the elements of the sequence
/// </typeparam>
/// <param name="sequence">
/// The sequence to exclude elements from
/// </param>
/// <param name="range">
/// The zero-based index at which to begin excluding elements
/// </param>
/// <returns>
/// A sequence that excludes the specified portion of elements
/// </returns>
/// <exception cref="ArgumentNullException">
/// <paramref name="sequence"/> is <see langword="null" />.
/// </exception>
/// <exception cref="ArgumentOutOfRangeException">
/// <paramref name="range.Start.Value"/> or <paramref name="range.End.Value"/> is less than <c>0</c>.
/// </exception>
/// <remarks>
/// This method uses deferred execution and streams its results.
/// </remarks>
public static IEnumerable<T> Exclude<T>(this IEnumerable<T> sequence, Range range)
{
ArgumentNullException.ThrowIfNull(sequence);

var startFromEnd = range.Start.IsFromEnd;
var endFromEnd = range.End.IsFromEnd;
if ((startFromEnd, endFromEnd) == (false, false))
{
return Exclude(sequence, range.Start.Value, range.End.Value - range.Start.Value);
}

if (sequence.TryGetCollectionCount() is int count)
{
var (start, length) = range.GetOffsetAndLength(count);
return Exclude(sequence, start, length);
}

return (startFromEnd, endFromEnd) switch
{
(false, true) => ExcludeEndFromEnd(sequence, range),
(true, false) => ExcludeStartFromEnd(sequence, range),
(true, true) when range.Start.Value < range.End.Value =>
ThrowHelper.ThrowArgumentOutOfRangeException<IEnumerable<T>>("length"),
_ => ExcludeRange(sequence, range),
};
}

/// <summary>
/// Represents an iterator for excluding elements from a collection.
/// </summary>
/// <typeparam name="T">Specifies the type of elements in the collection.</typeparam>
private sealed class ExcludeCollectionIterator<T>(ICollection<T> source, int startIndex, int count)
: CollectionIterator<T>
{
/// <summary>
/// Gets the number of elements in the source collection after excluding the specified portion of elements.
/// </summary>
public override int Count =>
source.Count < startIndex ? source.Count :
source.Count < startIndex + count ? startIndex :
source.Count - count;

/// <inheritdoc cref="IEnumerable{T}" />
protected override IEnumerable<T> GetEnumerable() =>
ExcludeCore(source, startIndex, count);
}

private sealed class ExcludeListIterator<T>(
IList<T> source,
int startIndex,
int count
) : ListIterator<T>
private sealed class ExcludeListIterator<T>(IList<T> source, int startIndex, int count)
: ListIterator<T>
{
/// <summary>
/// Gets the number of elements in the source collection after excluding the specified portion of elements.
/// </summary>
public override int Count =>
source.Count < startIndex ? source.Count :
source.Count < startIndex + count ? startIndex :
source.Count - count;

/// <inheritdoc cref="IEnumerable{T}" />
protected override IEnumerable<T> GetEnumerable()
{
var cnt = (uint)source.Count;
Expand All @@ -82,6 +137,7 @@ protected override IEnumerable<T> GetEnumerable()
yield return source[i];
}

/// <inheritdoc cref="IEnumerable{T}"/>
protected override T ElementAt(int index)
{
ArgumentOutOfRangeException.ThrowIfNegative(index);
Expand All @@ -102,7 +158,81 @@ private static IEnumerable<T> ExcludeCore<T>(IEnumerable<T> sequence, int startI
{
if (index < startIndex || index >= endIndex)
yield return item;

index++;
}
}

private static IEnumerable<T> ExcludeRange<T>(IEnumerable<T> sequence, Range range)
{
var start = range.Start.Value;
var queue = new Queue<T>(start + 1);
foreach (var e in sequence)
{
queue.Enqueue(e);
if (queue.Count > start)
yield return queue.Dequeue();
}

start = Math.Min(start, queue.Count);
var length = start - range.End.Value;
while (length > 0)
{
if (!queue.TryDequeue(out var _))
yield break;
length--;
}

while (queue.TryDequeue(out var element))
yield return element;
}

private static IEnumerable<T> ExcludeStartFromEnd<T>(IEnumerable<T> sequence, Range range)
{
var count = 0;
var start = range.Start.Value;
var queue = new Queue<T>(start + 1);
foreach (var e in sequence)
{
count++;
queue.Enqueue(e);
if (queue.Count > start)
yield return queue.Dequeue();
}

start = Math.Max(range.Start.GetOffset(count), 0);
var length = range.End.Value - start;

while (length > 0)
{
if (!queue.TryDequeue(out var _))
yield break;
length--;
}

while (queue.TryDequeue(out var element))
yield return element;
}

private static IEnumerable<T> ExcludeEndFromEnd<T>(IEnumerable<T> sequence, Range range)
{
var count = 0;
var start = range.Start.Value;
var end = range.End.Value;
var queue = new Queue<T>(end + 1);
foreach (var e in sequence)
{
count++;
queue.Enqueue(e);
if (queue.Count > end)
{
var el = queue.Dequeue();
if ((count - end) <= start)
yield return el;
}
}

while (queue.TryDequeue(out var element))
yield return element;
}
}
1 change: 1 addition & 0 deletions Source/SuperLinq/PublicAPI/net6.0/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ static SuperLinq.SuperEnumerable.DenseRank<TSource>(this System.Collections.Gene
static SuperLinq.SuperEnumerable.DenseRank<TSource>(this System.Collections.Generic.IEnumerable<TSource>! source, System.Collections.Generic.IComparer<TSource>! comparer, SuperLinq.OrderByDirection sortDirection) -> System.Collections.Generic.IEnumerable<(TSource item, int rank)>!
static SuperLinq.SuperEnumerable.DenseRankBy<TSource, TKey>(this System.Collections.Generic.IEnumerable<TSource>! source, System.Func<TSource, TKey>! keySelector, SuperLinq.OrderByDirection sortDirection) -> System.Collections.Generic.IEnumerable<(TSource item, int rank)>!
static SuperLinq.SuperEnumerable.DenseRankBy<TSource, TKey>(this System.Collections.Generic.IEnumerable<TSource>! source, System.Func<TSource, TKey>! keySelector, System.Collections.Generic.IComparer<TKey>! comparer, SuperLinq.OrderByDirection sortDirection) -> System.Collections.Generic.IEnumerable<(TSource item, int rank)>!
static SuperLinq.SuperEnumerable.Exclude<T>(this System.Collections.Generic.IEnumerable<T>! sequence, System.Range range) -> System.Collections.Generic.IEnumerable<T>!
static SuperLinq.SuperEnumerable.FullOuterHashJoin<TLeft, TRight, TKey, TResult>(this System.Collections.Generic.IEnumerable<TLeft>! left, System.Collections.Generic.IEnumerable<TRight>! right, System.Func<TLeft, TKey>! leftKeySelector, System.Func<TRight, TKey>! rightKeySelector, System.Func<TLeft, TResult>! leftResultSelector, System.Func<TRight, TResult>! rightResultSelector, System.Func<TLeft, TRight, TResult>! bothResultSelector, System.Collections.Generic.IEqualityComparer<TKey>? comparer = null) -> System.Collections.Generic.IEnumerable<TResult>!
static SuperLinq.SuperEnumerable.FullOuterHashJoin<TLeft, TRight, TKey>(this System.Collections.Generic.IEnumerable<TLeft>! left, System.Collections.Generic.IEnumerable<TRight>! right, System.Func<TLeft, TKey>! leftKeySelector, System.Func<TRight, TKey>! rightKeySelector, System.Collections.Generic.IEqualityComparer<TKey>? comparer = null) -> System.Collections.Generic.IEnumerable<(TLeft? Left, TRight? Right)>!
static SuperLinq.SuperEnumerable.FullOuterMergeJoin<TLeft, TRight, TKey, TResult>(this System.Collections.Generic.IEnumerable<TLeft>! left, System.Collections.Generic.IEnumerable<TRight>! right, System.Func<TLeft, TKey>! leftKeySelector, System.Func<TRight, TKey>! rightKeySelector, System.Func<TLeft, TResult>! leftResultSelector, System.Func<TRight, TResult>! rightResultSelector, System.Func<TLeft, TRight, TResult>! bothResultSelector, System.Collections.Generic.IComparer<TKey>? comparer = null) -> System.Collections.Generic.IEnumerable<TResult>!
Expand Down
1 change: 1 addition & 0 deletions Source/SuperLinq/PublicAPI/net7.0/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ static SuperLinq.SuperEnumerable.DenseRank<TSource>(this System.Collections.Gene
static SuperLinq.SuperEnumerable.DenseRank<TSource>(this System.Collections.Generic.IEnumerable<TSource>! source, System.Collections.Generic.IComparer<TSource>! comparer, SuperLinq.OrderByDirection sortDirection) -> System.Collections.Generic.IEnumerable<(TSource item, int rank)>!
static SuperLinq.SuperEnumerable.DenseRankBy<TSource, TKey>(this System.Collections.Generic.IEnumerable<TSource>! source, System.Func<TSource, TKey>! keySelector, SuperLinq.OrderByDirection sortDirection) -> System.Collections.Generic.IEnumerable<(TSource item, int rank)>!
static SuperLinq.SuperEnumerable.DenseRankBy<TSource, TKey>(this System.Collections.Generic.IEnumerable<TSource>! source, System.Func<TSource, TKey>! keySelector, System.Collections.Generic.IComparer<TKey>! comparer, SuperLinq.OrderByDirection sortDirection) -> System.Collections.Generic.IEnumerable<(TSource item, int rank)>!
static SuperLinq.SuperEnumerable.Exclude<T>(this System.Collections.Generic.IEnumerable<T>! sequence, System.Range range) -> System.Collections.Generic.IEnumerable<T>!
static SuperLinq.SuperEnumerable.FullOuterHashJoin<TLeft, TRight, TKey, TResult>(this System.Collections.Generic.IEnumerable<TLeft>! left, System.Collections.Generic.IEnumerable<TRight>! right, System.Func<TLeft, TKey>! leftKeySelector, System.Func<TRight, TKey>! rightKeySelector, System.Func<TLeft, TResult>! leftResultSelector, System.Func<TRight, TResult>! rightResultSelector, System.Func<TLeft, TRight, TResult>! bothResultSelector, System.Collections.Generic.IEqualityComparer<TKey>? comparer = null) -> System.Collections.Generic.IEnumerable<TResult>!
static SuperLinq.SuperEnumerable.FullOuterHashJoin<TLeft, TRight, TKey>(this System.Collections.Generic.IEnumerable<TLeft>! left, System.Collections.Generic.IEnumerable<TRight>! right, System.Func<TLeft, TKey>! leftKeySelector, System.Func<TRight, TKey>! rightKeySelector, System.Collections.Generic.IEqualityComparer<TKey>? comparer = null) -> System.Collections.Generic.IEnumerable<(TLeft? Left, TRight? Right)>!
static SuperLinq.SuperEnumerable.FullOuterMergeJoin<TLeft, TRight, TKey, TResult>(this System.Collections.Generic.IEnumerable<TLeft>! left, System.Collections.Generic.IEnumerable<TRight>! right, System.Func<TLeft, TKey>! leftKeySelector, System.Func<TRight, TKey>! rightKeySelector, System.Func<TLeft, TResult>! leftResultSelector, System.Func<TRight, TResult>! rightResultSelector, System.Func<TLeft, TRight, TResult>! bothResultSelector, System.Collections.Generic.IComparer<TKey>? comparer = null) -> System.Collections.Generic.IEnumerable<TResult>!
Expand Down
1 change: 1 addition & 0 deletions Source/SuperLinq/PublicAPI/net8.0/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ static SuperLinq.SuperEnumerable.DenseRank<TSource>(this System.Collections.Gene
static SuperLinq.SuperEnumerable.DenseRank<TSource>(this System.Collections.Generic.IEnumerable<TSource>! source, System.Collections.Generic.IComparer<TSource>! comparer, SuperLinq.OrderByDirection sortDirection) -> System.Collections.Generic.IEnumerable<(TSource item, int rank)>!
static SuperLinq.SuperEnumerable.DenseRankBy<TSource, TKey>(this System.Collections.Generic.IEnumerable<TSource>! source, System.Func<TSource, TKey>! keySelector, SuperLinq.OrderByDirection sortDirection) -> System.Collections.Generic.IEnumerable<(TSource item, int rank)>!
static SuperLinq.SuperEnumerable.DenseRankBy<TSource, TKey>(this System.Collections.Generic.IEnumerable<TSource>! source, System.Func<TSource, TKey>! keySelector, System.Collections.Generic.IComparer<TKey>! comparer, SuperLinq.OrderByDirection sortDirection) -> System.Collections.Generic.IEnumerable<(TSource item, int rank)>!
static SuperLinq.SuperEnumerable.Exclude<T>(this System.Collections.Generic.IEnumerable<T>! sequence, System.Range range) -> System.Collections.Generic.IEnumerable<T>!
static SuperLinq.SuperEnumerable.FullOuterHashJoin<TLeft, TRight, TKey, TResult>(this System.Collections.Generic.IEnumerable<TLeft>! left, System.Collections.Generic.IEnumerable<TRight>! right, System.Func<TLeft, TKey>! leftKeySelector, System.Func<TRight, TKey>! rightKeySelector, System.Func<TLeft, TResult>! leftResultSelector, System.Func<TRight, TResult>! rightResultSelector, System.Func<TLeft, TRight, TResult>! bothResultSelector, System.Collections.Generic.IEqualityComparer<TKey>? comparer = null) -> System.Collections.Generic.IEnumerable<TResult>!
static SuperLinq.SuperEnumerable.FullOuterHashJoin<TLeft, TRight, TKey>(this System.Collections.Generic.IEnumerable<TLeft>! left, System.Collections.Generic.IEnumerable<TRight>! right, System.Func<TLeft, TKey>! leftKeySelector, System.Func<TRight, TKey>! rightKeySelector, System.Collections.Generic.IEqualityComparer<TKey>? comparer = null) -> System.Collections.Generic.IEnumerable<(TLeft? Left, TRight? Right)>!
static SuperLinq.SuperEnumerable.FullOuterMergeJoin<TLeft, TRight, TKey, TResult>(this System.Collections.Generic.IEnumerable<TLeft>! left, System.Collections.Generic.IEnumerable<TRight>! right, System.Func<TLeft, TKey>! leftKeySelector, System.Func<TRight, TKey>! rightKeySelector, System.Func<TLeft, TResult>! leftResultSelector, System.Func<TRight, TResult>! rightResultSelector, System.Func<TLeft, TRight, TResult>! bothResultSelector, System.Collections.Generic.IComparer<TKey>? comparer = null) -> System.Collections.Generic.IEnumerable<TResult>!
Expand Down
1 change: 1 addition & 0 deletions Source/SuperLinq/PublicAPI/net9.0/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ static SuperLinq.SuperEnumerable.DenseRank<TSource>(this System.Collections.Gene
static SuperLinq.SuperEnumerable.DenseRank<TSource>(this System.Collections.Generic.IEnumerable<TSource>! source, System.Collections.Generic.IComparer<TSource>! comparer, SuperLinq.OrderByDirection sortDirection) -> System.Collections.Generic.IEnumerable<(TSource item, int rank)>!
static SuperLinq.SuperEnumerable.DenseRankBy<TSource, TKey>(this System.Collections.Generic.IEnumerable<TSource>! source, System.Func<TSource, TKey>! keySelector, SuperLinq.OrderByDirection sortDirection) -> System.Collections.Generic.IEnumerable<(TSource item, int rank)>!
static SuperLinq.SuperEnumerable.DenseRankBy<TSource, TKey>(this System.Collections.Generic.IEnumerable<TSource>! source, System.Func<TSource, TKey>! keySelector, System.Collections.Generic.IComparer<TKey>! comparer, SuperLinq.OrderByDirection sortDirection) -> System.Collections.Generic.IEnumerable<(TSource item, int rank)>!
static SuperLinq.SuperEnumerable.Exclude<T>(this System.Collections.Generic.IEnumerable<T>! sequence, System.Range range) -> System.Collections.Generic.IEnumerable<T>!
static SuperLinq.SuperEnumerable.FullOuterHashJoin<TLeft, TRight, TKey, TResult>(this System.Collections.Generic.IEnumerable<TLeft>! left, System.Collections.Generic.IEnumerable<TRight>! right, System.Func<TLeft, TKey>! leftKeySelector, System.Func<TRight, TKey>! rightKeySelector, System.Func<TLeft, TResult>! leftResultSelector, System.Func<TRight, TResult>! rightResultSelector, System.Func<TLeft, TRight, TResult>! bothResultSelector, System.Collections.Generic.IEqualityComparer<TKey>? comparer = null) -> System.Collections.Generic.IEnumerable<TResult>!
static SuperLinq.SuperEnumerable.FullOuterHashJoin<TLeft, TRight, TKey>(this System.Collections.Generic.IEnumerable<TLeft>! left, System.Collections.Generic.IEnumerable<TRight>! right, System.Func<TLeft, TKey>! leftKeySelector, System.Func<TRight, TKey>! rightKeySelector, System.Collections.Generic.IEqualityComparer<TKey>? comparer = null) -> System.Collections.Generic.IEnumerable<(TLeft? Left, TRight? Right)>!
static SuperLinq.SuperEnumerable.FullOuterMergeJoin<TLeft, TRight, TKey, TResult>(this System.Collections.Generic.IEnumerable<TLeft>! left, System.Collections.Generic.IEnumerable<TRight>! right, System.Func<TLeft, TKey>! leftKeySelector, System.Func<TRight, TKey>! rightKeySelector, System.Func<TLeft, TResult>! leftResultSelector, System.Func<TRight, TResult>! rightResultSelector, System.Func<TLeft, TRight, TResult>! bothResultSelector, System.Collections.Generic.IComparer<TKey>? comparer = null) -> System.Collections.Generic.IEnumerable<TResult>!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ static SuperLinq.SuperEnumerable.DenseRank<TSource>(this System.Collections.Gene
static SuperLinq.SuperEnumerable.DenseRank<TSource>(this System.Collections.Generic.IEnumerable<TSource>! source, System.Collections.Generic.IComparer<TSource>! comparer, SuperLinq.OrderByDirection sortDirection) -> System.Collections.Generic.IEnumerable<(TSource item, int rank)>!
static SuperLinq.SuperEnumerable.DenseRankBy<TSource, TKey>(this System.Collections.Generic.IEnumerable<TSource>! source, System.Func<TSource, TKey>! keySelector, SuperLinq.OrderByDirection sortDirection) -> System.Collections.Generic.IEnumerable<(TSource item, int rank)>!
static SuperLinq.SuperEnumerable.DenseRankBy<TSource, TKey>(this System.Collections.Generic.IEnumerable<TSource>! source, System.Func<TSource, TKey>! keySelector, System.Collections.Generic.IComparer<TKey>! comparer, SuperLinq.OrderByDirection sortDirection) -> System.Collections.Generic.IEnumerable<(TSource item, int rank)>!
static SuperLinq.SuperEnumerable.Exclude<T>(this System.Collections.Generic.IEnumerable<T>! sequence, System.Range range) -> System.Collections.Generic.IEnumerable<T>!
static SuperLinq.SuperEnumerable.FullOuterHashJoin<TLeft, TRight, TKey, TResult>(this System.Collections.Generic.IEnumerable<TLeft>! left, System.Collections.Generic.IEnumerable<TRight>! right, System.Func<TLeft, TKey>! leftKeySelector, System.Func<TRight, TKey>! rightKeySelector, System.Func<TLeft, TResult>! leftResultSelector, System.Func<TRight, TResult>! rightResultSelector, System.Func<TLeft, TRight, TResult>! bothResultSelector, System.Collections.Generic.IEqualityComparer<TKey>? comparer = null) -> System.Collections.Generic.IEnumerable<TResult>!
static SuperLinq.SuperEnumerable.FullOuterHashJoin<TLeft, TRight, TKey>(this System.Collections.Generic.IEnumerable<TLeft>! left, System.Collections.Generic.IEnumerable<TRight>! right, System.Func<TLeft, TKey>! leftKeySelector, System.Func<TRight, TKey>! rightKeySelector, System.Collections.Generic.IEqualityComparer<TKey>? comparer = null) -> System.Collections.Generic.IEnumerable<(TLeft? Left, TRight? Right)>!
static SuperLinq.SuperEnumerable.FullOuterMergeJoin<TLeft, TRight, TKey, TResult>(this System.Collections.Generic.IEnumerable<TLeft>! left, System.Collections.Generic.IEnumerable<TRight>! right, System.Func<TLeft, TKey>! leftKeySelector, System.Func<TRight, TKey>! rightKeySelector, System.Func<TLeft, TResult>! leftResultSelector, System.Func<TRight, TResult>! rightResultSelector, System.Func<TLeft, TRight, TResult>! bothResultSelector, System.Collections.Generic.IComparer<TKey>? comparer = null) -> System.Collections.Generic.IEnumerable<TResult>!
Expand Down
Loading

0 comments on commit 635691e

Please sign in to comment.