Skip to content

Commit 5205ea2

Browse files
leandromohatifaziz
authored andcommitted
Add IndexBy
This is a squashed merge of PR #562 that closes #560.
1 parent 531fe71 commit 5205ea2

File tree

4 files changed

+263
-0
lines changed

4 files changed

+263
-0
lines changed

MoreLinq.Test/IndexByTest.cs

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
#region License and Terms
2+
// MoreLINQ - Extensions to LINQ to Objects
3+
// Copyright (c) 2019 Leandro F. Vieira (leandromoh). All rights reserved.
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the "License");
6+
// you may not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
#endregion
17+
18+
namespace MoreLinq.Test
19+
{
20+
using System;
21+
using NUnit.Framework;
22+
23+
[TestFixture]
24+
public class IndexByTest
25+
{
26+
[Test]
27+
public void IndexBySimpleTest()
28+
{
29+
var source = new[] { "ana", "beatriz", "carla", "bob", "davi", "adriano", "angelo", "carlos" };
30+
var result = source.IndexBy(x => x.First());
31+
32+
result.AssertSequenceEqual(
33+
KeyValuePair.Create(0, "ana" ),
34+
KeyValuePair.Create(0, "beatriz"),
35+
KeyValuePair.Create(0, "carla" ),
36+
KeyValuePair.Create(1, "bob" ),
37+
KeyValuePair.Create(0, "davi" ),
38+
KeyValuePair.Create(1, "adriano"),
39+
KeyValuePair.Create(2, "angelo" ),
40+
KeyValuePair.Create(1, "carlos" ));
41+
}
42+
43+
[Test]
44+
public void IndexByWithSecondOccurenceImmediatelyAfterFirst()
45+
{
46+
var result = "jaffer".IndexBy(c => c);
47+
48+
result.AssertSequenceEqual(
49+
KeyValuePair.Create(0, 'j'),
50+
KeyValuePair.Create(0, 'a'),
51+
KeyValuePair.Create(0, 'f'),
52+
KeyValuePair.Create(1, 'f'),
53+
KeyValuePair.Create(0, 'e'),
54+
KeyValuePair.Create(0, 'r'));
55+
}
56+
57+
[Test]
58+
public void IndexByWithEqualityComparer()
59+
{
60+
var source = new[] { "a", "B", "c", "A", "b", "A" };
61+
var result = source.IndexBy(c => c, StringComparer.OrdinalIgnoreCase);
62+
63+
result.AssertSequenceEqual(
64+
KeyValuePair.Create(0, "a"),
65+
KeyValuePair.Create(0, "B"),
66+
KeyValuePair.Create(0, "c"),
67+
KeyValuePair.Create(1, "A"),
68+
KeyValuePair.Create(1, "b"),
69+
KeyValuePair.Create(2, "A"));
70+
}
71+
72+
[Test]
73+
public void IndexByIsLazy()
74+
{
75+
new BreakingSequence<string>().IndexBy(BreakingFunc.Of<string, char>());
76+
}
77+
78+
[Test]
79+
public void IndexByWithSomeNullKeys()
80+
{
81+
var source = new[] { "foo", null, "bar", "baz", null, null, "baz", "bar", null, "foo" };
82+
var result = source.IndexBy(c => c);
83+
84+
const string @null = null; // type inference happiness
85+
result.AssertSequenceEqual(
86+
KeyValuePair.Create(0, "foo"),
87+
KeyValuePair.Create(0, @null),
88+
KeyValuePair.Create(0, "bar"),
89+
KeyValuePair.Create(0, "baz"),
90+
KeyValuePair.Create(1, @null),
91+
KeyValuePair.Create(2, @null),
92+
KeyValuePair.Create(1, "baz"),
93+
KeyValuePair.Create(1, "bar"),
94+
KeyValuePair.Create(3, @null),
95+
KeyValuePair.Create(1, "foo"));
96+
}
97+
98+
[Test]
99+
public void IndexBytDoesNotIterateUnnecessaryElements()
100+
{
101+
var source = MoreEnumerable.From(() => "ana",
102+
() => "beatriz",
103+
() => "carla",
104+
() => "bob",
105+
() => "davi",
106+
() => throw new TestException(),
107+
() => "angelo",
108+
() => "carlos");
109+
110+
var result = source.IndexBy(x => x.First());
111+
112+
result.Take(5).AssertSequenceEqual(
113+
KeyValuePair.Create(0, "ana" ),
114+
KeyValuePair.Create(0, "beatriz"),
115+
KeyValuePair.Create(0, "carla" ),
116+
KeyValuePair.Create(1, "bob" ),
117+
KeyValuePair.Create(0, "davi" ));
118+
119+
Assert.Throws<TestException>(() =>
120+
result.ElementAt(5));
121+
}
122+
}
123+
}

MoreLinq/Extensions.g.cs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2847,6 +2847,60 @@ public static IEnumerable<KeyValuePair<int, TSource>> Index<TSource>(this IEnume
28472847

28482848
}
28492849

2850+
/// <summary><c>IndexBy</c> extension.</summary>
2851+
2852+
[GeneratedCode("MoreLinq.ExtensionsGenerator", "1.0.0.0")]
2853+
public static partial class IndexByExtension
2854+
{
2855+
/// <summary>
2856+
/// Applies a key-generating function to each element of a sequence and
2857+
/// returns a sequence that contains the elements of the original
2858+
/// sequence as well its key and index inside the group of its key.
2859+
/// </summary>
2860+
/// <typeparam name="TSource">Type of the source sequence elements.</typeparam>
2861+
/// <typeparam name="TKey">Type of the projected key.</typeparam>
2862+
/// <param name="source">Source sequence.</param>
2863+
/// <param name="keySelector">
2864+
/// Function that projects the key given an element in the source sequence.</param>
2865+
/// <returns>
2866+
/// A sequence of elements paired with their index within the key-group.
2867+
/// The index is the key and the element is the value of the pair.
2868+
/// </returns>
2869+
2870+
public static IEnumerable<KeyValuePair<int, TSource>>
2871+
IndexBy<TSource, TKey>(
2872+
this IEnumerable<TSource> source,
2873+
Func<TSource, TKey> keySelector) => MoreEnumerable. IndexBy(source, keySelector);
2874+
2875+
/// <summary>
2876+
/// Applies a key-generating function to each element of a sequence and
2877+
/// returns a sequence that contains the elements of the original
2878+
/// sequence as well its key and index inside the group of its key.
2879+
/// An additional parameter specifies a comparer to use for testing the
2880+
/// equivalence of keys.
2881+
/// </summary>
2882+
/// <typeparam name="TSource">Type of the source sequence elements.</typeparam>
2883+
/// <typeparam name="TKey">Type of the projected key.</typeparam>
2884+
/// <param name="source">Source sequence.</param>
2885+
/// <param name="keySelector">
2886+
/// Function that projects the key given an element in the source sequence.</param>
2887+
/// <param name="comparer">
2888+
/// The equality comparer to use to determine whether or not keys are
2889+
/// equal. If <c>null</c>, the default equality comparer for
2890+
/// <typeparamref name="TSource"/> is used.</param>
2891+
/// <returns>
2892+
/// A sequence of elements paired with their index within the key-group.
2893+
/// The index is the key and the element is the value of the pair.
2894+
/// </returns>
2895+
2896+
public static IEnumerable<KeyValuePair<int, TSource>>
2897+
IndexBy<TSource, TKey>(
2898+
this IEnumerable<TSource> source,
2899+
Func<TSource, TKey> keySelector,
2900+
IEqualityComparer<TKey> comparer) => MoreEnumerable. IndexBy(source, keySelector, comparer);
2901+
2902+
}
2903+
28502904
/// <summary><c>Insert</c> extension.</summary>
28512905

28522906
[GeneratedCode("MoreLinq.ExtensionsGenerator", "1.0.0.0")]

MoreLinq/IndexBy.cs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#region License and Terms
2+
// MoreLINQ - Extensions to LINQ to Objects
3+
// Copyright (c) 2019 Leandro F. Vieira (leandromoh). All rights reserved.
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the "License");
6+
// you may not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
#endregion
17+
18+
namespace MoreLinq
19+
{
20+
using System;
21+
using System.Collections.Generic;
22+
using System.Linq;
23+
24+
static partial class MoreEnumerable
25+
{
26+
/// <summary>
27+
/// Applies a key-generating function to each element of a sequence and
28+
/// returns a sequence that contains the elements of the original
29+
/// sequence as well its key and index inside the group of its key.
30+
/// </summary>
31+
/// <typeparam name="TSource">Type of the source sequence elements.</typeparam>
32+
/// <typeparam name="TKey">Type of the projected key.</typeparam>
33+
/// <param name="source">Source sequence.</param>
34+
/// <param name="keySelector">
35+
/// Function that projects the key given an element in the source sequence.</param>
36+
/// <returns>
37+
/// A sequence of elements paired with their index within the key-group.
38+
/// The index is the key and the element is the value of the pair.
39+
/// </returns>
40+
41+
public static IEnumerable<KeyValuePair<int, TSource>>
42+
IndexBy<TSource, TKey>(
43+
this IEnumerable<TSource> source,
44+
Func<TSource, TKey> keySelector) =>
45+
source.IndexBy(keySelector, null);
46+
47+
/// <summary>
48+
/// Applies a key-generating function to each element of a sequence and
49+
/// returns a sequence that contains the elements of the original
50+
/// sequence as well its key and index inside the group of its key.
51+
/// An additional parameter specifies a comparer to use for testing the
52+
/// equivalence of keys.
53+
/// </summary>
54+
/// <typeparam name="TSource">Type of the source sequence elements.</typeparam>
55+
/// <typeparam name="TKey">Type of the projected key.</typeparam>
56+
/// <param name="source">Source sequence.</param>
57+
/// <param name="keySelector">
58+
/// Function that projects the key given an element in the source sequence.</param>
59+
/// <param name="comparer">
60+
/// The equality comparer to use to determine whether or not keys are
61+
/// equal. If <c>null</c>, the default equality comparer for
62+
/// <typeparamref name="TSource"/> is used.</param>
63+
/// <returns>
64+
/// A sequence of elements paired with their index within the key-group.
65+
/// The index is the key and the element is the value of the pair.
66+
/// </returns>
67+
68+
public static IEnumerable<KeyValuePair<int, TSource>>
69+
IndexBy<TSource, TKey>(
70+
this IEnumerable<TSource> source,
71+
Func<TSource, TKey> keySelector,
72+
IEqualityComparer<TKey> comparer) =>
73+
from e in source.ScanBy(keySelector, k => (Index: -1, Item: default(TSource)), (s, k, e) => (s.Index + 1, e), comparer)
74+
select new KeyValuePair<int, TSource>(e.Value.Index, e.Value.Item);
75+
}
76+
}

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,16 @@ the source sequence.
323323

324324
This method has 2 overloads.
325325

326+
### IndexBy
327+
328+
329+
Applies a key-generating function to each element of a sequence and returns
330+
a sequence that contains the elements of the original sequence as well its
331+
key and index inside the group of its key. An additional argument specifies
332+
a comparer to use for testing equivalence of keys.
333+
334+
This method has 2 overloads.
335+
326336
### Insert
327337

328338
Inserts the elements of a sequence into another sequence at a specified index.

0 commit comments

Comments
 (0)