Skip to content
50 changes: 49 additions & 1 deletion Source/SuperLinq/Move.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,52 @@ static IEnumerable<T> Core(IEnumerable<T> source, int bufferStartIndex, int buff
yield return e.Current;
}
}
}

/// <summary>
/// Returns a sequence with a range of elements in the source sequence moved to a new offset.
/// </summary>
/// <typeparam name="T">
/// Type of the source sequence.
/// </typeparam>
/// <param name="source">
/// The source sequence.
/// </param>
/// <param name="range">
/// The range of values to move.
/// </param>
/// <param name="toIndex">
/// The index where the specified range will be moved.</param>
/// <returns>
/// A sequence with the specified range moved to the new position.
/// </returns>
/// <exception cref="ArgumentNullException">
/// <paramref name="source"/> is <see langword="null"/>.
/// </exception>
/// <exception cref="ArgumentOutOfRangeException">
/// <paramref name="range"/>'s start is less than <c>0</c> or <paramref name="range"/>'s end is before start in the sequence.
/// </exception>
/// <remarks>
/// This operator uses deferred executing and streams its results.
/// </remarks>
public static IEnumerable<T> Move<T>(this IEnumerable<T> source, Range range, Index toIndex)
{
int? length = 0;
if (range.Start.IsFromEnd || range.End.IsFromEnd || toIndex.IsFromEnd)
{
length = source.TryGetCollectionCount();
if (!length.HasValue)
{
length = source.GetCollectionCount();
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if TryGetCollectionCount() fails to return a value, then GetCollectionCount() will throw an exception. try calling Enumerable.Range(1, 10).Select(Identity).GetCollectionCount().

}
}
var fromIndex = range.Start.IsFromEnd ? range.Start.GetOffset(length.Value) : range.Start.Value;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

var count = (range.End.IsFromEnd ? range.End.GetOffset(length.Value) : range.End.Value) - fromIndex;
var to = toIndex.IsFromEnd ? toIndex.GetOffset(length.Value) : toIndex.Value;
return source.Move
(
fromIndex,
count,
to
);
}
}
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 @@ -16,6 +16,7 @@
static SuperLinq.SuperEnumerable.Batch<TSource, TResult>(this System.Collections.Generic.IEnumerable<TSource>! source, int size, System.Func<System.ArraySegment<TSource>, TResult>! resultSelector) -> System.Collections.Generic.IEnumerable<TResult>!
static SuperLinq.SuperEnumerable.Batch<TSource, TResult>(this System.Collections.Generic.IEnumerable<TSource>! source, TSource[]! array, int size, System.Func<System.ArraySegment<TSource>, TResult>! resultSelector) -> System.Collections.Generic.IEnumerable<TResult>!
static SuperLinq.SuperEnumerable.Batch<TSource, TResult>(this System.Collections.Generic.IEnumerable<TSource>! source, TSource[]! array, System.Func<System.ArraySegment<TSource>, TResult>! resultSelector) -> System.Collections.Generic.IEnumerable<TResult>!
static SuperLinq.SuperEnumerable.Move<T>(this System.Collections.Generic.IEnumerable<T>! source, System.Range range, System.Index toIndex) -> System.Collections.Generic.IEnumerable<T>!
static SuperLinq.SuperEnumerable.Window<TSource, TResult>(this System.Collections.Generic.IEnumerable<TSource>! source, int size, System.Func<System.ArraySegment<TSource>, TResult>! selector) -> System.Collections.Generic.IEnumerable<TResult>!
static SuperLinq.SuperEnumerable.Window<TSource, TResult>(this System.Collections.Generic.IEnumerable<TSource>! source, TSource[]! array, int size, System.Func<System.ArraySegment<TSource>, TResult>! selector) -> System.Collections.Generic.IEnumerable<TResult>!
static SuperLinq.SuperEnumerable.Window<TSource, TResult>(this System.Collections.Generic.IEnumerable<TSource>! source, TSource[]! array, System.Func<System.ArraySegment<TSource>, TResult>! selector) -> 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 @@ -16,6 +16,7 @@
static SuperLinq.SuperEnumerable.Batch<TSource, TResult>(this System.Collections.Generic.IEnumerable<TSource>! source, int size, System.Func<System.ArraySegment<TSource>, TResult>! resultSelector) -> System.Collections.Generic.IEnumerable<TResult>!
static SuperLinq.SuperEnumerable.Batch<TSource, TResult>(this System.Collections.Generic.IEnumerable<TSource>! source, TSource[]! array, int size, System.Func<System.ArraySegment<TSource>, TResult>! resultSelector) -> System.Collections.Generic.IEnumerable<TResult>!
static SuperLinq.SuperEnumerable.Batch<TSource, TResult>(this System.Collections.Generic.IEnumerable<TSource>! source, TSource[]! array, System.Func<System.ArraySegment<TSource>, TResult>! resultSelector) -> System.Collections.Generic.IEnumerable<TResult>!
static SuperLinq.SuperEnumerable.Move<T>(this System.Collections.Generic.IEnumerable<T>! source, System.Range range, System.Index toIndex) -> System.Collections.Generic.IEnumerable<T>!
static SuperLinq.SuperEnumerable.Window<TSource, TResult>(this System.Collections.Generic.IEnumerable<TSource>! source, int size, System.Func<System.ArraySegment<TSource>, TResult>! selector) -> System.Collections.Generic.IEnumerable<TResult>!
static SuperLinq.SuperEnumerable.Window<TSource, TResult>(this System.Collections.Generic.IEnumerable<TSource>! source, TSource[]! array, int size, System.Func<System.ArraySegment<TSource>, TResult>! selector) -> System.Collections.Generic.IEnumerable<TResult>!
static SuperLinq.SuperEnumerable.Window<TSource, TResult>(this System.Collections.Generic.IEnumerable<TSource>! source, TSource[]! array, System.Func<System.ArraySegment<TSource>, TResult>! selector) -> 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 @@ -16,6 +16,7 @@
static SuperLinq.SuperEnumerable.Batch<TSource, TResult>(this System.Collections.Generic.IEnumerable<TSource>! source, int size, System.Func<System.ArraySegment<TSource>, TResult>! resultSelector) -> System.Collections.Generic.IEnumerable<TResult>!
static SuperLinq.SuperEnumerable.Batch<TSource, TResult>(this System.Collections.Generic.IEnumerable<TSource>! source, TSource[]! array, int size, System.Func<System.ArraySegment<TSource>, TResult>! resultSelector) -> System.Collections.Generic.IEnumerable<TResult>!
static SuperLinq.SuperEnumerable.Batch<TSource, TResult>(this System.Collections.Generic.IEnumerable<TSource>! source, TSource[]! array, System.Func<System.ArraySegment<TSource>, TResult>! resultSelector) -> System.Collections.Generic.IEnumerable<TResult>!
static SuperLinq.SuperEnumerable.Move<T>(this System.Collections.Generic.IEnumerable<T>! source, System.Range range, System.Index toIndex) -> System.Collections.Generic.IEnumerable<T>!
static SuperLinq.SuperEnumerable.Window<TSource, TResult>(this System.Collections.Generic.IEnumerable<TSource>! source, int size, System.Func<System.ArraySegment<TSource>, TResult>! selector) -> System.Collections.Generic.IEnumerable<TResult>!
static SuperLinq.SuperEnumerable.Window<TSource, TResult>(this System.Collections.Generic.IEnumerable<TSource>! source, TSource[]! array, int size, System.Func<System.ArraySegment<TSource>, TResult>! selector) -> System.Collections.Generic.IEnumerable<TResult>!
static SuperLinq.SuperEnumerable.Window<TSource, TResult>(this System.Collections.Generic.IEnumerable<TSource>! source, TSource[]! array, System.Func<System.ArraySegment<TSource>, TResult>! selector) -> System.Collections.Generic.IEnumerable<TResult>!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
static SuperLinq.SuperEnumerable.Batch<TSource, TResult>(this System.Collections.Generic.IEnumerable<TSource>! source, int size, System.Func<System.ArraySegment<TSource>, TResult>! resultSelector) -> System.Collections.Generic.IEnumerable<TResult>!
static SuperLinq.SuperEnumerable.Batch<TSource, TResult>(this System.Collections.Generic.IEnumerable<TSource>! source, TSource[]! array, int size, System.Func<System.ArraySegment<TSource>, TResult>! resultSelector) -> System.Collections.Generic.IEnumerable<TResult>!
static SuperLinq.SuperEnumerable.Batch<TSource, TResult>(this System.Collections.Generic.IEnumerable<TSource>! source, TSource[]! array, System.Func<System.ArraySegment<TSource>, TResult>! resultSelector) -> System.Collections.Generic.IEnumerable<TResult>!
static SuperLinq.SuperEnumerable.Move<T>(this System.Collections.Generic.IEnumerable<T>! source, System.Range range, System.Index toIndex) -> System.Collections.Generic.IEnumerable<T>!
static SuperLinq.SuperEnumerable.Window<TSource, TResult>(this System.Collections.Generic.IEnumerable<TSource>! source, int size, System.Func<System.ArraySegment<TSource>, TResult>! selector) -> System.Collections.Generic.IEnumerable<TResult>!
static SuperLinq.SuperEnumerable.Window<TSource, TResult>(this System.Collections.Generic.IEnumerable<TSource>! source, TSource[]! array, int size, System.Func<System.ArraySegment<TSource>, TResult>! selector) -> System.Collections.Generic.IEnumerable<TResult>!
static SuperLinq.SuperEnumerable.Window<TSource, TResult>(this System.Collections.Generic.IEnumerable<TSource>! source, TSource[]! array, System.Func<System.ArraySegment<TSource>, TResult>! selector) -> System.Collections.Generic.IEnumerable<TResult>!
Expand Down
95 changes: 94 additions & 1 deletion Tests/SuperLinq.Test/MoveTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,40 @@ public void MoveWithNegativeFromIndex()
new[] { 1 }.Move(-1, 0, 0));
}

[Fact]
public void MoveRangeWithNegativeStartIndex()
{
_ = Assert.Throws<ArgumentOutOfRangeException>(() => new[] { 1 }.Move(-1..-1, 0));
}

[Fact]
public void MoveWithNegativeCount()
{
_ = Assert.Throws<ArgumentOutOfRangeException>(() =>
new[] { 1 }.Move(0, -1, 0));
}

[Fact]
public void MoveRangeWithDecendingRange()
{
_ = Assert.Throws<ArgumentOutOfRangeException>(() =>
new[] { 1 }.Move(0..-1, 0));
}

[Fact]
public void MoveWithNegativeToIndex()
{
_ = Assert.Throws<ArgumentOutOfRangeException>(() =>
new[] { 1 }.Move(0, 0, -1));
}

[Fact]
public void MoveRangeWithNegativeToIndex()
{
_ = Assert.Throws<ArgumentOutOfRangeException>(() =>
new[] { 1 }.Move(0..0, -1));
}

[Fact]
public void MoveIsLazy()
{
Expand All @@ -44,6 +64,21 @@ public void Move(int length, int fromIndex, int count, int toIndex)
result.AssertSequenceEqual(expectations);
}

[Theory, MemberData(nameof(MoveRangeSource))]
public void MoveRange(int length, Range range, int toIndex)
{
var source = Enumerable.Range(0, length);

using var test = source.AsTestingSequence();

var result = test.Move(range, toIndex);

var slice = source.Take(range);
var exclude = source.Exclude(range.Start.Value, range.End.Value - range.Start.Value);
var expectations = exclude.Take(toIndex).Concat(slice).Concat(exclude.Skip(toIndex));
result.AssertSequenceEqual(expectations);
}

public static IEnumerable<object[]> MoveSource()
{
const int Length = 10;
Expand All @@ -58,6 +93,20 @@ from count in Enumerable.Range(0, Length + 1)
select tcd;
}

public static IEnumerable<object[]> MoveRangeSource()
{
const int Length = 10;
return
from index in Enumerable.Range(0, Length)
from count in Enumerable.Range(0, Length + 1)
from tcd in new object[][]
{
[Length, index..(index + count), Math.Max(0, index - 1),],
[Length, index..(index + count), index + 1,],
}
select tcd;
}

[Theory, MemberData(nameof(MoveWithSequenceShorterThanToIndexSource))]
public void MoveWithSequenceShorterThanToIndex(int length, int fromIndex, int count, int toIndex)
{
Expand All @@ -71,10 +120,27 @@ public void MoveWithSequenceShorterThanToIndex(int length, int fromIndex, int co
Assert.Equal(expectations, result);
}

[Theory, MemberData(nameof(MoveRangeWithSequenceShorterThanToIndexSource))]
public void MoveRangeWithSequenceShorterThanToIndex(int length, Range range, int toIndex)
{
var source = Enumerable.Range(0, length);

using var test = source.AsTestingSequence();

var result = test.Move(range, toIndex);

var expectations = source.Exclude(range.Start.Value, range.End.Value - range.Start.Value).Concat(source.Take(range));
Assert.Equal(expectations, result);
}

public static IEnumerable<object[]> MoveWithSequenceShorterThanToIndexSource() =>
Enumerable.Range(10, 10 + 5)
.Select(toIndex => new object[] { 10, 5, 2, toIndex, });

public static IEnumerable<object[]> MoveRangeWithSequenceShorterThanToIndexSource() =>
Enumerable.Range(10, 10 + 5)
.Select(toIndex => new object[] { 10, 5..7, toIndex, });

[Fact]
public void MoveIsRepeatable()
{
Expand All @@ -84,6 +150,15 @@ public void MoveIsRepeatable()
Assert.Equal(result, result.ToArray());
}

[Fact]
public void MoveRangeIsRepeatable()
{
using var source = Enumerable.Range(0, 10).AsTestingSequence(maxEnumerations: 2);

var result = source.Move(0..5, 10);
Assert.Equal(result, result.ToArray());
}

[Fact]
public void MoveWithFromIndexEqualsToIndex()
{
Expand All @@ -93,6 +168,15 @@ public void MoveWithFromIndexEqualsToIndex()
result.AssertSequenceEqual(Enumerable.Range(0, 10));
}

[Fact]
public void MoveRangeWithFomrIndexEqualsToIndex()
{
using var source = Enumerable.Range(0, 10).AsTestingSequence();

var result = source.Move(5..1004, 5);
result.AssertSequenceEqual(Enumerable.Range(0, 10));
}

[Fact]
public void MoveWithCountEqualsZero()
{
Expand All @@ -101,4 +185,13 @@ public void MoveWithCountEqualsZero()
var result = source.Move(5, 0, 999);
result.AssertSequenceEqual(Enumerable.Range(0, 10));
}
}

[Fact]
public void MoveRngeWithCountEqualsZero()
{
using var source = Enumerable.Range(0, 10).AsTestingSequence();

var result = source.Move(5..5, 999);
result.AssertSequenceEqual(Enumerable.Range(0, 10));
}
}