Skip to content

Commit 1cbaa5c

Browse files
committed
New SkipAtOrDefault extension method, SkipAt now behaves similar to existing ElementAt LINQ extension method
1 parent c1d2739 commit 1cbaa5c

3 files changed

Lines changed: 129 additions & 18 deletions

File tree

DotNetExtensions/Collections/SkipAt.cs

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ public static partial class EnumerableExtensions
44
{
55
/// <summary>
66
/// Returns a new sequence that skips the element at the specified index.
7+
/// Throws <see cref="ArgumentOutOfRangeException"/> if the index is negative or outside the bounds of the source.
78
/// </summary>
89
/// <typeparam name="T">The type of elements in the source sequence.</typeparam>
910
/// <param name="source">The source sequence.</param>
@@ -15,7 +16,7 @@ public static partial class EnumerableExtensions
1516
/// Thrown if the <paramref name="source"/> is <c>null</c>.
1617
/// </exception>
1718
/// <exception cref="ArgumentOutOfRangeException">
18-
/// Thrown if the <paramref name="index"/> is negative.
19+
/// Thrown if the <paramref name="index"/> is negative or outside the bounds of the source.
1920
/// </exception>
2021
public static IEnumerable<T> SkipAt<T>(this IEnumerable<T> source, int index)
2122
{
@@ -26,6 +27,46 @@ public static IEnumerable<T> SkipAt<T>(this IEnumerable<T> source, int index)
2627
}
2728

2829
private static IEnumerable<T> SkipAtInternal<T>(IEnumerable<T> source, int index)
30+
{
31+
using var enumerator = source.GetEnumerator();
32+
var i = 0;
33+
var found = false;
34+
35+
while (enumerator.MoveNext())
36+
{
37+
if (i++ == index)
38+
{
39+
found = true;
40+
continue;
41+
}
42+
43+
yield return enumerator.Current;
44+
}
45+
46+
if (!found)
47+
throw new ArgumentOutOfRangeException(nameof(index), "Index is out of range of the source sequence.");
48+
}
49+
50+
/// <summary>
51+
/// Returns a new sequence that skips the element at the specified index.
52+
/// If the index is negative or outside the bounds of the source, the original sequence is returned unchanged.
53+
/// </summary>
54+
/// <typeparam name="T">The type of elements in the source sequence.</typeparam>
55+
/// <param name="source">The source sequence.</param>
56+
/// <param name="index">The index of the element to skip.</param>
57+
/// <returns>
58+
/// A new sequence without the element at the specified index, or the original sequence if the index is out of range.
59+
/// </returns>
60+
/// <exception cref="ArgumentNullException">
61+
/// Thrown if the <paramref name="source"/> is <c>null</c>.
62+
/// </exception>
63+
public static IEnumerable<T> SkipAtOrDefault<T>(this IEnumerable<T> source, int index)
64+
{
65+
ArgumentNullException.ThrowIfNull(source);
66+
return index < 0 ? source : SkipAtOrDefaultInternal(source, index);
67+
}
68+
69+
private static IEnumerable<T> SkipAtOrDefaultInternal<T>(IEnumerable<T> source, int index)
2970
{
3071
using var enumerator = source.GetEnumerator();
3172
var i = 0;

DotNetExtensions/DotNetExtensions.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
</PropertyGroup>
99

1010
<PropertyGroup>
11-
<Version>5.3.0</Version>
11+
<Version>5.4.0</Version>
1212
<Authors>Diogo Medeiros</Authors>
1313
<Company>Diogo Medeiros</Company>
1414
<Description>This is a collection of extension methods for .NET Core.</Description>

DotNetExtensionsTests/Collections/EnumerableExtensionsTests.cs

Lines changed: 86 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public void TestPairwiseWithOneElement()
3232
public void TestPairwiseWithNoElements()
3333
{
3434
int[] source = [];
35-
var result = source.Pairwise().ToImmutableArray();
35+
var result = source.Pairwise().ToArray();
3636

3737
Assert.AreEqual(0, result.Length);
3838
}
@@ -55,33 +55,103 @@ public void TestAdjacentWithInvalidLength()
5555
int[] source = [1, 2, 3, 4, 5];
5656
var result = source.Adjacent(6).ToArray();
5757

58-
Assert.AreEqual(0, result.Length);
58+
Assert.AreEqual([], result);
59+
}
60+
61+
[TestMethod]
62+
public void TestAdjacentWithZeroLength()
63+
{
64+
int[] source = [1, 2, 3, 4, 5];
65+
var result = source.Adjacent(0).ToArray();
66+
67+
Assert.AreEqual([], result);
68+
}
69+
70+
[TestMethod]
71+
public void TestAdjacentWithLengthOne()
72+
{
73+
int[] source = [1, 2, 3, 4, 5];
74+
var result = source.Adjacent(1).ToArray();
75+
76+
var expected = ImmutableArray.Create<int[]>([1], [2], [3], [4], [5]);
77+
CollectionAssert.AreEqual(expected, result);
78+
}
79+
80+
[TestMethod]
81+
public void TestAdjacentWithLengthEqualToSource()
82+
{
83+
int[] source = [1, 2, 3, 4, 5];
84+
var result = source.Adjacent(5).ToArray();
85+
86+
Assert.AreEqual(1, result.Length);
87+
CollectionAssert.AreEqual(source, result[0]);
5988
}
6089

6190
[TestMethod]
6291
public void TestSkipAtWithValidIndex()
6392
{
6493
int[] source = [1, 2, 3, 4, 5];
65-
var result = source.SkipAt(4).ToImmutableArray();
94+
var result = source.SkipAt(4).ToArray();
95+
var expected = ImmutableArray.Create(1, 2, 3, 4);
6696

67-
Assert.AreEqual(4, result.Length);
68-
Assert.AreEqual(1, result[0]);
69-
Assert.AreEqual(2, result[1]);
70-
Assert.AreEqual(3, result[2]);
71-
Assert.AreEqual(4, result[3]);
97+
CollectionAssert.AreEqual(expected, result);
7298
}
7399

74100
[TestMethod]
75101
public void TestSkipAtWithInvalidIndex()
76102
{
77103
int[] source = [1, 2, 3, 4, 5];
78-
var result = source.SkipAt(5).ToImmutableArray();
79-
80-
Assert.AreEqual(5, result.Length);
81-
Assert.AreEqual(1, result[0]);
82-
Assert.AreEqual(2, result[1]);
83-
Assert.AreEqual(3, result[2]);
84-
Assert.AreEqual(4, result[3]);
85-
Assert.AreEqual(5, result[4]);
104+
Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => source.SkipAt(5).ToArray());
105+
}
106+
107+
[TestMethod]
108+
public void TestSkipAtWithNegativeIndex()
109+
{
110+
int[] source = [1, 2, 3, 4, 5];
111+
Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => source.SkipAt(-1).ToArray());
112+
}
113+
114+
[TestMethod]
115+
public void TestSkipAtWithEmptySource()
116+
{
117+
int[] source = [];
118+
Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => source.SkipAt(0).ToArray());
119+
}
120+
121+
[TestMethod]
122+
public void TestSkipAtOrDefaultWithValidIndex()
123+
{
124+
int[] source = [1, 2, 3, 4, 5];
125+
var result = source.SkipAtOrDefault(2).ToArray();
126+
var expected = new[] { 1, 2, 4, 5 };
127+
128+
CollectionAssert.AreEqual(expected, result);
129+
}
130+
131+
[TestMethod]
132+
public void TestSkipAtOrDefaultWithNegativeIndex()
133+
{
134+
int[] source = [1, 2, 3, 4, 5];
135+
var result = source.SkipAtOrDefault(-1).ToArray();
136+
137+
CollectionAssert.AreEqual(source, result);
138+
}
139+
140+
[TestMethod]
141+
public void TestSkipAtOrDefaultWithOutOfRangeIndex()
142+
{
143+
int[] source = [1, 2, 3, 4, 5];
144+
var result = source.SkipAtOrDefault(5).ToArray();
145+
146+
CollectionAssert.AreEqual(source, result);
147+
}
148+
149+
[TestMethod]
150+
public void TestSkipAtOrDefaultWithEmptySource()
151+
{
152+
int[] source = [];
153+
var result = source.SkipAtOrDefault(0).ToArray();
154+
155+
Assert.AreEqual(source, result);
86156
}
87157
}

0 commit comments

Comments
 (0)