Skip to content

Commit 4538269

Browse files
Merge pull request #12 from thomaslevesque/optimize-withindex
Optimize WithIndex
2 parents 0386096 + d09b091 commit 4538269

File tree

10 files changed

+481
-36
lines changed

10 files changed

+481
-36
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@ api/
77
docs/api/
88
*.user
99
tools/build/ToolPaths.cs
10-
10+
BenchmarkDotNet.Artifacts/

Linq.Extras.sln

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{777D6312
2323
EndProject
2424
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "build", "tools\build\build.csproj", "{750F9598-6DAE-4036-B4D3-EFFA7A37FF74}"
2525
EndProject
26+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Linq.Extras.Benchmarks", "tests\Linq.Extras.Benchmarks\Linq.Extras.Benchmarks.csproj", "{0FFABA2D-D2B5-48BF-B9A4-C4AA4A7D7C92}"
27+
EndProject
2628
Global
2729
GlobalSection(SolutionConfigurationPlatforms) = preSolution
2830
Debug|Any CPU = Debug|Any CPU
@@ -41,6 +43,10 @@ Global
4143
{86E2B0AA-E343-4846-8661-E09365D68C99}.Release|Any CPU.ActiveCfg = Release|Any CPU
4244
{750F9598-6DAE-4036-B4D3-EFFA7A37FF74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
4345
{750F9598-6DAE-4036-B4D3-EFFA7A37FF74}.Release|Any CPU.ActiveCfg = Release|Any CPU
46+
{0FFABA2D-D2B5-48BF-B9A4-C4AA4A7D7C92}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
47+
{0FFABA2D-D2B5-48BF-B9A4-C4AA4A7D7C92}.Debug|Any CPU.Build.0 = Debug|Any CPU
48+
{0FFABA2D-D2B5-48BF-B9A4-C4AA4A7D7C92}.Release|Any CPU.ActiveCfg = Release|Any CPU
49+
{0FFABA2D-D2B5-48BF-B9A4-C4AA4A7D7C92}.Release|Any CPU.Build.0 = Release|Any CPU
4450
EndGlobalSection
4551
GlobalSection(SolutionProperties) = preSolution
4652
HideSolutionNode = FALSE

src/Linq.Extras/Internal/ArgumentExtensions.cs

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Runtime.CompilerServices;
23
using JetBrains.Annotations;
34

45
namespace Linq.Extras.Internal
@@ -8,6 +9,7 @@ static class ArgumentExtensions
89
// ReSharper disable UnusedParameter.Global
910

1011
[ContractAnnotation("value:null => halt")]
12+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
1113
public static void CheckArgumentNull<T>(
1214
[NoEnumeration] this T value,
1315
[InvokerParameterName] string paramName)
@@ -17,6 +19,7 @@ public static void CheckArgumentNull<T>(
1719
throw new ArgumentNullException(paramName);
1820
}
1921

22+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2023
public static void CheckArgumentOutOfRange<T>(
2124
[NotNull] this T value,
2225
[InvokerParameterName] string paramName,
@@ -30,18 +33,6 @@ public static void CheckArgumentOutOfRange<T>(
3033

3134
// will uncomment if necessary
3235

33-
//public static void CheckArgumentOutOfRange<T>(
34-
// [NotNull] this T value,
35-
// [InvokerParameterName] string paramName,
36-
// T min,
37-
// T max,
38-
// string message)
39-
// where T : IComparable<T>
40-
//{
41-
// if (value.CompareTo(min) < 0 || value.CompareTo(max) > 0)
42-
// throw new ArgumentOutOfRangeException(paramName, message);
43-
//}
44-
4536
//public static void CheckArgumentInEnum(
4637
// this Enum value,
4738
// [InvokerParameterName] string paramName)

src/Linq.Extras/IndexedItem.cs renamed to src/Linq.Extras/ItemWithIndex.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@ namespace Linq.Extras
77
/// </summary>
88
/// <typeparam name="T">The type of the element.</typeparam>
99
[PublicAPI]
10-
public readonly struct IndexedItem<T>
10+
public readonly struct ItemWithIndex<T>
1111
{
1212
/// <summary>
13-
/// Initializes a new instance of <see cref="IndexedItem{T}"/>.
13+
/// Initializes a new instance of <see cref="ItemWithIndex{T}"/>.
1414
/// </summary>
1515
/// <param name="item"></param>
1616
/// <param name="index"></param>
17-
public IndexedItem(T item, int index)
17+
public ItemWithIndex(T item, int index)
1818
{
1919
Item = item;
2020
Index = index;
@@ -31,7 +31,7 @@ public IndexedItem(T item, int index)
3131
public int Index { get; }
3232

3333
/// <summary>
34-
/// Deconstructs this <see cref="IndexedItem{T}"/> into its item and index.
34+
/// Deconstructs this <see cref="ItemWithIndex{T}"/> into its item and index.
3535
/// </summary>
3636
/// <param name="item">The item.</param>
3737
/// <param name="index">The index.</param>

src/Linq.Extras/WithIndex.cs

Lines changed: 248 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
using System.Collections.Generic;
1+
using System;
2+
using System.Collections;
3+
using System.Collections.Generic;
24
using System.Linq;
35
using JetBrains.Annotations;
46
using Linq.Extras.Internal;
@@ -14,10 +16,39 @@ partial class XEnumerable
1416
/// <param name="source">The source sequence.</param>
1517
/// <returns>A sequence of elements paired with their index in the sequence.</returns>
1618
[Pure]
17-
public static IEnumerable<IndexedItem<TSource>> WithIndex<TSource>(
19+
public static WithIndexEnumerable<TSource> WithIndex<TSource>(
1820
[NotNull] this IEnumerable<TSource> source)
1921
{
20-
return source.Select((item, index) => new IndexedItem<TSource>(item, index));
22+
source.CheckArgumentNull(nameof(source));
23+
return new WithIndexEnumerable<TSource>(source);
24+
}
25+
26+
/// <summary>
27+
/// Associates an index to each element of the source array.
28+
/// </summary>
29+
/// <typeparam name="TSource">The type of the elements of <c>source</c>.</typeparam>
30+
/// <param name="source">The source array.</param>
31+
/// <returns>A sequence of elements paired with their index in the array.</returns>
32+
[Pure]
33+
public static WithIndexArrayEnumerable<TSource> WithIndex<TSource>(
34+
[NotNull] this TSource[] source)
35+
{
36+
source.CheckArgumentNull(nameof(source));
37+
return new WithIndexArrayEnumerable<TSource>(source);
38+
}
39+
40+
/// <summary>
41+
/// Associates an index to each element of the source <see cref="List{T}"/>.
42+
/// </summary>
43+
/// <typeparam name="TSource">The type of the elements of <c>source</c>.</typeparam>
44+
/// <param name="source">The source list.</param>
45+
/// <returns>A sequence of elements paired with their index in the list.</returns>
46+
[Pure]
47+
public static WithIndexListEnumerable<TSource> WithIndex<TSource>(
48+
[NotNull] this List<TSource> source)
49+
{
50+
source.CheckArgumentNull(nameof(source));
51+
return new WithIndexListEnumerable<TSource>(source);
2152
}
2253

2354
/// <summary>
@@ -28,9 +59,222 @@ public static IEnumerable<IndexedItem<TSource>> WithIndex<TSource>(
2859
/// <returns>A sequence of elements without their associated indexes.</returns>
2960
[Pure]
3061
public static IEnumerable<TSource> WithoutIndex<TSource>(
31-
[NotNull] this IEnumerable<IndexedItem<TSource>> source)
62+
[NotNull] this IEnumerable<ItemWithIndex<TSource>> source)
3263
{
3364
return source.Select(indexed => indexed.Item);
3465
}
66+
67+
/// <summary>
68+
/// A sequence of elements associated with their index.
69+
/// </summary>
70+
/// <typeparam name="TSource">The type of the elements of the source sequence.</typeparam>
71+
public struct WithIndexEnumerable<TSource> : IEnumerable<ItemWithIndex<TSource>>
72+
{
73+
private readonly IEnumerable<TSource> _source;
74+
75+
internal WithIndexEnumerable(IEnumerable<TSource> source)
76+
{
77+
_source = source;
78+
}
79+
80+
/// <summary>
81+
/// Returns an enumerator that iterates through the <see cref="WithIndexEnumerable{TSource}"/>.
82+
/// </summary>
83+
/// <returns>An enumerator that iterates through the <see cref="WithIndexEnumerable{TSource}"/>.</returns>
84+
public Enumerator GetEnumerator() => new Enumerator(_source.GetEnumerator());
85+
86+
IEnumerator<ItemWithIndex<TSource>> IEnumerable<ItemWithIndex<TSource>>.GetEnumerator() => GetEnumerator();
87+
88+
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
89+
90+
/// <summary>
91+
/// Enumerates the elements of a <see cref="WithIndexEnumerable{TSource}"/>.
92+
/// </summary>
93+
public struct Enumerator : IEnumerator<ItemWithIndex<TSource>>
94+
{
95+
private readonly IEnumerator<TSource> _innerEnumerator;
96+
private int _index;
97+
98+
internal Enumerator(IEnumerator<TSource> inner)
99+
{
100+
_innerEnumerator = inner;
101+
_index = -1;
102+
}
103+
104+
/// <summary>
105+
/// Advances the enumerator to the next element of the <see cref="WithIndexEnumerable{TSource}"/>.
106+
/// </summary>
107+
/// <returns>true if the enumerator was successfully advanced to the next element; false if the enumerator has passed the end of the collection.</returns>
108+
public bool MoveNext()
109+
{
110+
if (_innerEnumerator.MoveNext())
111+
{
112+
_index++;
113+
return true;
114+
}
115+
116+
return false;
117+
}
118+
119+
void IEnumerator.Reset() => _innerEnumerator.Reset();
120+
121+
/// <summary>
122+
/// Releases all resources used by the <see cref="Enumerator"/>.
123+
/// </summary>
124+
public void Dispose() => _innerEnumerator.Dispose();
125+
126+
/// <summary>
127+
/// Gets the element at the current position of the enumerator.
128+
/// </summary>
129+
public ItemWithIndex<TSource> Current => new ItemWithIndex<TSource>(_innerEnumerator.Current, _index);
130+
131+
object IEnumerator.Current => Current;
132+
}
133+
}
134+
135+
/// <summary>
136+
/// A sequence of elements associated with their index, optimized for the case when the underlying collection is an array.
137+
/// </summary>
138+
/// <typeparam name="TSource">The type of the elements of the source array.</typeparam>
139+
public struct WithIndexArrayEnumerable<TSource> : IEnumerable<ItemWithIndex<TSource>>
140+
{
141+
private readonly TSource[] _array;
142+
143+
internal WithIndexArrayEnumerable(TSource[] array)
144+
{
145+
_array = array;
146+
}
147+
148+
/// <summary>
149+
/// Returns an enumerator that iterates through the <see cref="WithIndexArrayEnumerable{TSource}"/>.
150+
/// </summary>
151+
/// <returns>An enumerator that iterates through the <see cref="WithIndexArrayEnumerable{TSource}"/>.</returns>
152+
public Enumerator GetEnumerator() => new Enumerator(_array);
153+
154+
IEnumerator<ItemWithIndex<TSource>> IEnumerable<ItemWithIndex<TSource>>.GetEnumerator() => GetEnumerator();
155+
156+
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
157+
158+
/// <summary>
159+
/// Enumerates the elements of a <see cref="WithIndexArrayEnumerable{TSource}"/>.
160+
/// </summary>
161+
public struct Enumerator : IEnumerator<ItemWithIndex<TSource>>
162+
{
163+
private readonly TSource[] _array;
164+
private int _index;
165+
166+
internal Enumerator(TSource[] array)
167+
{
168+
_array = array;
169+
_index = -1;
170+
}
171+
172+
/// <summary>
173+
/// Advances the enumerator to the next element of the <see cref="WithIndexArrayEnumerable{TSource}"/>.
174+
/// </summary>
175+
/// <returns>true if the enumerator was successfully advanced to the next element; false if the enumerator has passed the end of the collection.</returns>
176+
public bool MoveNext()
177+
{
178+
if (_index + 1 < _array.Length)
179+
{
180+
_index++;
181+
return true;
182+
}
183+
184+
return false;
185+
}
186+
187+
void IEnumerator.Reset()
188+
{
189+
_index = -1;
190+
}
191+
192+
/// <summary>
193+
/// Releases all resources used by the <see cref="Enumerator"/>.
194+
/// </summary>
195+
public void Dispose()
196+
{
197+
}
198+
199+
/// <summary>
200+
/// Gets the element at the current position of the enumerator.
201+
/// </summary>
202+
public ItemWithIndex<TSource> Current => new ItemWithIndex<TSource>(_array[_index], _index);
203+
204+
object IEnumerator.Current => throw new NotImplementedException();
205+
}
206+
}
207+
208+
/// <summary>
209+
/// A sequence of elements associated with their index, optimized for the case when the underlying collection is a <see cref="List{T}"/>.
210+
/// </summary>
211+
/// <typeparam name="TSource">The type of the elements of the source list.</typeparam>
212+
public struct WithIndexListEnumerable<TSource> : IEnumerable<ItemWithIndex<TSource>>
213+
{
214+
private readonly List<TSource> _list;
215+
216+
internal WithIndexListEnumerable(List<TSource> list)
217+
{
218+
_list = list;
219+
}
220+
221+
/// <summary>
222+
/// Returns an enumerator that iterates through the <see cref="WithIndexListEnumerable{TSource}"/>.
223+
/// </summary>
224+
/// <returns>An enumerator that iterates through the <see cref="WithIndexListEnumerable{TSource}"/>.</returns>
225+
public Enumerator GetEnumerator() => new Enumerator(_list.GetEnumerator());
226+
227+
IEnumerator<ItemWithIndex<TSource>> IEnumerable<ItemWithIndex<TSource>>.GetEnumerator() => GetEnumerator();
228+
229+
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
230+
231+
/// <summary>
232+
/// Enumerates the elements of a <see cref="WithIndexListEnumerable{TSource}"/>.
233+
/// </summary>
234+
public struct Enumerator : IEnumerator<ItemWithIndex<TSource>>
235+
{
236+
private List<TSource>.Enumerator _inner;
237+
private int _index;
238+
239+
internal Enumerator(List<TSource>.Enumerator inner)
240+
{
241+
_inner = inner;
242+
_index = -1;
243+
}
244+
245+
/// <summary>
246+
/// Advances the enumerator to the next element of the <see cref="WithIndexListEnumerable{TSource}"/>.
247+
/// </summary>
248+
/// <returns>true if the enumerator was successfully advanced to the next element; false if the enumerator has passed the end of the collection.</returns>
249+
public bool MoveNext()
250+
{
251+
if (_inner.MoveNext())
252+
{
253+
_index++;
254+
return true;
255+
}
256+
257+
return false;
258+
}
259+
260+
void IEnumerator.Reset()
261+
{
262+
_index = -1;
263+
((IEnumerator)_inner).Reset();
264+
}
265+
266+
/// <summary>
267+
/// Releases all resources used by the <see cref="Enumerator"/>.
268+
/// </summary>
269+
public void Dispose() => _inner.Dispose();
270+
271+
/// <summary>
272+
/// Gets the element at the current position of the enumerator.
273+
/// </summary>
274+
public ItemWithIndex<TSource> Current => new ItemWithIndex<TSource>(_inner.Current, _index);
275+
276+
object IEnumerator.Current => throw new NotImplementedException();
277+
}
278+
}
35279
}
36280
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>netstandard2.0</TargetFramework>
5+
</PropertyGroup>
6+
7+
<ItemGroup>
8+
<PackageReference Include="BenchmarkDotNet" Version="0.12.0" />
9+
</ItemGroup>
10+
11+
<ItemGroup>
12+
<ProjectReference Include="..\..\src\Linq.Extras\Linq.Extras.csproj" />
13+
</ItemGroup>
14+
15+
</Project>

0 commit comments

Comments
 (0)