Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support for passing Range as a parameter to .Exclude. #619

Merged
merged 15 commits into from
Feb 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@
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 @@
yield return source[i];
}

/// <inheritdoc cref="IEnumerable{T}"/>
protected override T ElementAt(int index)
{
ArgumentOutOfRangeException.ThrowIfNegative(index);
Expand All @@ -102,7 +158,81 @@
{
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;

Check warning on line 182 in Source/SuperLinq/Exclude.cs

View check run for this annotation

Codecov / codecov/patch

Source/SuperLinq/Exclude.cs#L182

Added line #L182 was not covered by tests
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
Loading