diff --git a/MoreLinq.Test/IndexByTest.cs b/MoreLinq.Test/IndexByTest.cs new file mode 100644 index 000000000..2a02d7474 --- /dev/null +++ b/MoreLinq.Test/IndexByTest.cs @@ -0,0 +1,123 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2019 Leandro F. Vieira (leandromoh). All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq.Test +{ + using System; + using NUnit.Framework; + + [TestFixture] + public class IndexByTest + { + [Test] + public void IndexBySimpleTest() + { + var source = new[] { "ana", "beatriz", "carla", "bob", "davi", "adriano", "angelo", "carlos" }; + var result = source.IndexBy(x => x.First()); + + result.AssertSequenceEqual( + KeyValuePair.Create(0, "ana" ), + KeyValuePair.Create(0, "beatriz"), + KeyValuePair.Create(0, "carla" ), + KeyValuePair.Create(1, "bob" ), + KeyValuePair.Create(0, "davi" ), + KeyValuePair.Create(1, "adriano"), + KeyValuePair.Create(2, "angelo" ), + KeyValuePair.Create(1, "carlos" )); + } + + [Test] + public void IndexByWithSecondOccurenceImmediatelyAfterFirst() + { + var result = "jaffer".IndexBy(c => c); + + result.AssertSequenceEqual( + KeyValuePair.Create(0, 'j'), + KeyValuePair.Create(0, 'a'), + KeyValuePair.Create(0, 'f'), + KeyValuePair.Create(1, 'f'), + KeyValuePair.Create(0, 'e'), + KeyValuePair.Create(0, 'r')); + } + + [Test] + public void IndexByWithEqualityComparer() + { + var source = new[] { "a", "B", "c", "A", "b", "A" }; + var result = source.IndexBy(c => c, StringComparer.OrdinalIgnoreCase); + + result.AssertSequenceEqual( + KeyValuePair.Create(0, "a"), + KeyValuePair.Create(0, "B"), + KeyValuePair.Create(0, "c"), + KeyValuePair.Create(1, "A"), + KeyValuePair.Create(1, "b"), + KeyValuePair.Create(2, "A")); + } + + [Test] + public void IndexByIsLazy() + { + new BreakingSequence().IndexBy(BreakingFunc.Of()); + } + + [Test] + public void IndexByWithSomeNullKeys() + { + var source = new[] { "foo", null, "bar", "baz", null, null, "baz", "bar", null, "foo" }; + var result = source.IndexBy(c => c); + + const string @null = null; // type inference happiness + result.AssertSequenceEqual( + KeyValuePair.Create(0, "foo"), + KeyValuePair.Create(0, @null), + KeyValuePair.Create(0, "bar"), + KeyValuePair.Create(0, "baz"), + KeyValuePair.Create(1, @null), + KeyValuePair.Create(2, @null), + KeyValuePair.Create(1, "baz"), + KeyValuePair.Create(1, "bar"), + KeyValuePair.Create(3, @null), + KeyValuePair.Create(1, "foo")); + } + + [Test] + public void IndexBytDoesNotIterateUnnecessaryElements() + { + var source = MoreEnumerable.From(() => "ana", + () => "beatriz", + () => "carla", + () => "bob", + () => "davi", + () => throw new TestException(), + () => "angelo", + () => "carlos"); + + var result = source.IndexBy(x => x.First()); + + result.Take(5).AssertSequenceEqual( + KeyValuePair.Create(0, "ana" ), + KeyValuePair.Create(0, "beatriz"), + KeyValuePair.Create(0, "carla" ), + KeyValuePair.Create(1, "bob" ), + KeyValuePair.Create(0, "davi" )); + + Assert.Throws(() => + result.ElementAt(5)); + } + } +} diff --git a/MoreLinq/Extensions.g.cs b/MoreLinq/Extensions.g.cs index 0c88c7700..9b3f26915 100644 --- a/MoreLinq/Extensions.g.cs +++ b/MoreLinq/Extensions.g.cs @@ -2847,6 +2847,60 @@ public static IEnumerable> Index(this IEnume } + /// IndexBy extension. + + [GeneratedCode("MoreLinq.ExtensionsGenerator", "1.0.0.0")] + public static partial class IndexByExtension + { + /// + /// Applies a key-generating function to each element of a sequence and + /// returns a sequence that contains the elements of the original + /// sequence as well its key and index inside the group of its key. + /// + /// Type of the source sequence elements. + /// Type of the projected key. + /// Source sequence. + /// + /// Function that projects the key given an element in the source sequence. + /// + /// A sequence of elements paired with their index within the key-group. + /// The index is the key and the element is the value of the pair. + /// + + public static IEnumerable> + IndexBy( + this IEnumerable source, + Func keySelector) => MoreEnumerable. IndexBy(source, keySelector); + + /// + /// Applies a key-generating function to each element of a sequence and + /// returns a sequence that contains the elements of the original + /// sequence as well its key and index inside the group of its key. + /// An additional parameter specifies a comparer to use for testing the + /// equivalence of keys. + /// + /// Type of the source sequence elements. + /// Type of the projected key. + /// Source sequence. + /// + /// Function that projects the key given an element in the source sequence. + /// + /// The equality comparer to use to determine whether or not keys are + /// equal. If null, the default equality comparer for + /// is used. + /// + /// A sequence of elements paired with their index within the key-group. + /// The index is the key and the element is the value of the pair. + /// + + public static IEnumerable> + IndexBy( + this IEnumerable source, + Func keySelector, + IEqualityComparer comparer) => MoreEnumerable. IndexBy(source, keySelector, comparer); + + } + /// Insert extension. [GeneratedCode("MoreLinq.ExtensionsGenerator", "1.0.0.0")] diff --git a/MoreLinq/IndexBy.cs b/MoreLinq/IndexBy.cs new file mode 100644 index 000000000..1a407b07c --- /dev/null +++ b/MoreLinq/IndexBy.cs @@ -0,0 +1,76 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2019 Leandro F. Vieira (leandromoh). All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + using System.Linq; + + static partial class MoreEnumerable + { + /// + /// Applies a key-generating function to each element of a sequence and + /// returns a sequence that contains the elements of the original + /// sequence as well its key and index inside the group of its key. + /// + /// Type of the source sequence elements. + /// Type of the projected key. + /// Source sequence. + /// + /// Function that projects the key given an element in the source sequence. + /// + /// A sequence of elements paired with their index within the key-group. + /// The index is the key and the element is the value of the pair. + /// + + public static IEnumerable> + IndexBy( + this IEnumerable source, + Func keySelector) => + source.IndexBy(keySelector, null); + + /// + /// Applies a key-generating function to each element of a sequence and + /// returns a sequence that contains the elements of the original + /// sequence as well its key and index inside the group of its key. + /// An additional parameter specifies a comparer to use for testing the + /// equivalence of keys. + /// + /// Type of the source sequence elements. + /// Type of the projected key. + /// Source sequence. + /// + /// Function that projects the key given an element in the source sequence. + /// + /// The equality comparer to use to determine whether or not keys are + /// equal. If null, the default equality comparer for + /// is used. + /// + /// A sequence of elements paired with their index within the key-group. + /// The index is the key and the element is the value of the pair. + /// + + public static IEnumerable> + IndexBy( + this IEnumerable source, + Func keySelector, + IEqualityComparer comparer) => + from e in source.ScanBy(keySelector, k => (Index: -1, Item: default(TSource)), (s, k, e) => (s.Index + 1, e), comparer) + select new KeyValuePair(e.Value.Index, e.Value.Item); + } +} diff --git a/README.md b/README.md index 135075030..59694ffb3 100644 --- a/README.md +++ b/README.md @@ -323,6 +323,16 @@ the source sequence. This method has 2 overloads. +### IndexBy + + +Applies a key-generating function to each element of a sequence and returns +a sequence that contains the elements of the original sequence as well its +key and index inside the group of its key. An additional argument specifies +a comparer to use for testing equivalence of keys. + +This method has 2 overloads. + ### Insert Inserts the elements of a sequence into another sequence at a specified index.