From 78390329fcf8d80d1f93451260971daa49fb7554 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Wed, 5 Aug 2020 18:02:18 +0200 Subject: [PATCH 01/32] Initial version SpillSpan (WIP) --- MoreLinq.Test/MoreLinq.Test.csproj | 1 + MoreLinq.Test/SpillSpanTest.cs | 109 ++++++++++++++++++ MoreLinq/Experimental/SpillSpan.cs | 175 +++++++++++++++++++++++++++++ 3 files changed, 285 insertions(+) create mode 100644 MoreLinq.Test/SpillSpanTest.cs create mode 100644 MoreLinq/Experimental/SpillSpan.cs diff --git a/MoreLinq.Test/MoreLinq.Test.csproj b/MoreLinq.Test/MoreLinq.Test.csproj index 4f9d06d1e..82aa50539 100644 --- a/MoreLinq.Test/MoreLinq.Test.csproj +++ b/MoreLinq.Test/MoreLinq.Test.csproj @@ -27,6 +27,7 @@ all + runtime; build; native; contentfiles; analyzers all diff --git a/MoreLinq.Test/SpillSpanTest.cs b/MoreLinq.Test/SpillSpanTest.cs new file mode 100644 index 000000000..6bc3d1825 --- /dev/null +++ b/MoreLinq.Test/SpillSpanTest.cs @@ -0,0 +1,109 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2012 Atif Aziz. 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 System.Globalization; + using System.Text.RegularExpressions; + using Kons; + using NUnit.Framework; + using Experimental; + + [TestFixture] + public class SpillSpanTest + { + [Test] + public void Csv1() + { + const string csv = @" + a,c,b + 1,3,2 + 4,6,5 + 7,9,8"; + + var result = + from rows in new[] + { + from line in Regex.Split(csv.Trim(), @"\r?\n") + select line.Split(',').Select(f => f.Trim()).ToArray() + } + from row in + rows.Index() + .SpillSpan( + r => r.Key == 0, + null, + r => r.Value, + (r, _) => r, + h => MoreEnumerable.Return(h.Index() + .ToDictionary(e => e.Value, e => e.Key)) + .SelectMany(d => new[] { "a", "b", "c" }, + (d, n) => d[n]) + .Select(i => new Func(s => s[i])) + .ToArray(), + (bs, r) => bs.Select(b => int.Parse(b(r.Value), CultureInfo.InvariantCulture)) + .Fold((a, b, c) => new { A = a, B = b, C = c })) + select row; + + Assert.That(result, Is.EqualTo(new[] + { + new { A = 1, B = 2, C = 3 }, + new { A = 4, B = 5, C = 6 }, + new { A = 7, B = 8, C = 9 }, + })); + } + + [Test] + public void CsvWithColumnsInCommentLines() + { + const string csv = @" + ; a = Column A + ; b = Column B + ; c = Column C + 1,2,3 + 4,5,6 + 7,8,9"; + + var result = + from e in + Regex.Split(csv.Trim(), @"\r?\n") + .Select(line => line.Trim()) + .SpillSpan(h => Regex.Match(h, @"^;\s*(\w+)") is var m & m.Success ? (true, m.Groups[1].Value) : default, + ConsList.Empty, + ConsList.Cons, + (a, h) => a.Prepend(h), + h => MoreEnumerable.Return(h.Reverse() + .Index() + .ToDictionary(e => e.Value, e => e.Key)) + .SelectMany(d => new[] { "a", "b", "c" }, + (d, n) => d[n]) + .Select(i => new Func(s => s[i])) + .ToArray(), + (bs, r) => new { Bindings = bs, Fields = r.Split(',') }) + select e.Bindings + .Select(b => int.Parse(b(e.Fields), CultureInfo.InvariantCulture)) + .Fold((a, b, c) => new { A = a, B = b, C = c }); + + Assert.That(result, Is.EqualTo(new[] + { + new { A = 1, B = 2, C = 3 }, + new { A = 4, B = 5, C = 6 }, + new { A = 7, B = 8, C = 9 }, + })); + } + } +} diff --git a/MoreLinq/Experimental/SpillSpan.cs b/MoreLinq/Experimental/SpillSpan.cs new file mode 100644 index 000000000..a2a6fc7a6 --- /dev/null +++ b/MoreLinq/Experimental/SpillSpan.cs @@ -0,0 +1,175 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2020 Atif Aziz. 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.Experimental +{ + using System; + using System.Collections.Generic; + + partial class ExperimentalEnumerable + { + /// + /// TODO + /// + + public static IEnumerable<(T Head, T Item)> + SpillSpan(this IEnumerable source) => + source.SpillSpan(h => h, ValueTuple.Create); + + /// + /// TODO + /// + + public static IEnumerable + SpillSpan( + this IEnumerable source, + Func resultSelector) => + source.SpillSpan(h => h, resultSelector); + + /// + /// TODO + /// + + public static IEnumerable + SpillSpan( + this IEnumerable source, + Func headerSelector, + Func resultSelector) => + source.SpillSpan(1, h => headerSelector(h[0]), + (h, e, _) => resultSelector(h, e)); + + /// + /// TODO + /// + + public static IEnumerable + SpillSpan( + this IEnumerable source, + int count, + Func, H> headerSelector, + Func resultSelector) => + source.SpillSpan(count, headerSelector, + (h, e, _) => resultSelector(h, e)); + + /// + /// TODO + /// + + public static IEnumerable + SpillSpan( + this IEnumerable source, + int count, + Func, H> headerSelector, + Func resultSelector) => + source.SpillSpan((e, i) => i < count, + headerSelector, + resultSelector); + + /// + /// TODO + /// + + public static IEnumerable + SpillSpan( + this IEnumerable source, + Func predicate, + Func, H> headerSelector, + Func resultSelector) => + source.SpillSpan((e, _) => predicate(e), + headerSelector, + (h, e, _) => resultSelector(h, e)); + + /// + /// TODO + /// + + public static IEnumerable + SpillSpan( + this IEnumerable source, + Func predicate, + Func, H> headerSelector, + Func resultSelector) + { + return _(); IEnumerable _() + { + using var e = source.GetEnumerator(); + if (!e.MoveNext()) + yield break; + var i = 0; + var items = new List(); + for (; predicate(e.Current, i); e.MoveNext(), i++) + items.Add(e.Current); + var header = headerSelector(items); + items = null; // available for collection by GC + i = 0; + do { yield return resultSelector(header, e.Current, i++); } + while (e.MoveNext()); + } + } + + /// + /// TODO + /// + + public static IEnumerable + SpillSpan( + this IEnumerable source, + Func predicate, + A empty, + Func seeder, + Func accumulator, + Func headerSelector, + Func resultSelector) => + source.SpillSpan(e => predicate(e) ? (true, e) : default, + empty, seeder, accumulator, headerSelector, resultSelector); + + /// + /// TODO + /// + + public static IEnumerable + SpillSpan( + this IEnumerable source, + Func chooser, + A empty, + Func seeder, + Func accumulator, + Func headerSelector, + Func resultSelector) + { + return _(); IEnumerable _() + { + using var e = source.GetEnumerator(); + if (!e.MoveNext()) + yield break; + var (span, fm) = chooser(e.Current); + var state = span ? seeder(fm) : empty; + if (span) + { + if (!e.MoveNext()) + yield break; + for (; chooser(e.Current) is (true, var m); e.MoveNext()) + state = accumulator(state, m); + } + var header = headerSelector(state); + state = default; // available for collection by GC + do { yield return resultSelector(header, e.Current); } + while (e.MoveNext()); + } + } + } +} From 359bc5670b6e81c0eda0855749c7da479dbea970 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Thu, 6 Aug 2020 09:40:37 +0200 Subject: [PATCH 02/32] Fix copyright year --- MoreLinq.Test/SpillSpanTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MoreLinq.Test/SpillSpanTest.cs b/MoreLinq.Test/SpillSpanTest.cs index 6bc3d1825..0ecf4c7e0 100644 --- a/MoreLinq.Test/SpillSpanTest.cs +++ b/MoreLinq.Test/SpillSpanTest.cs @@ -1,6 +1,6 @@ #region License and Terms // MoreLINQ - Extensions to LINQ to Objects -// Copyright (c) 2012 Atif Aziz. All rights reserved. +// Copyright (c) 2020 Atif Aziz. 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. From 1c02293dafbcf2f4c01542fbbf57b6b83f7bc891 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Thu, 6 Aug 2020 09:43:22 +0200 Subject: [PATCH 03/32] Remove Kons dependency --- MoreLinq.Test/MoreLinq.Test.csproj | 1 - MoreLinq.Test/SpillSpanTest.cs | 10 ++++------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/MoreLinq.Test/MoreLinq.Test.csproj b/MoreLinq.Test/MoreLinq.Test.csproj index 82aa50539..4f9d06d1e 100644 --- a/MoreLinq.Test/MoreLinq.Test.csproj +++ b/MoreLinq.Test/MoreLinq.Test.csproj @@ -27,7 +27,6 @@ all - runtime; build; native; contentfiles; analyzers all diff --git a/MoreLinq.Test/SpillSpanTest.cs b/MoreLinq.Test/SpillSpanTest.cs index 0ecf4c7e0..4841ffb12 100644 --- a/MoreLinq.Test/SpillSpanTest.cs +++ b/MoreLinq.Test/SpillSpanTest.cs @@ -20,7 +20,6 @@ namespace MoreLinq.Test using System; using System.Globalization; using System.Text.RegularExpressions; - using Kons; using NUnit.Framework; using Experimental; @@ -83,11 +82,10 @@ from e in Regex.Split(csv.Trim(), @"\r?\n") .Select(line => line.Trim()) .SpillSpan(h => Regex.Match(h, @"^;\s*(\w+)") is var m & m.Success ? (true, m.Groups[1].Value) : default, - ConsList.Empty, - ConsList.Cons, - (a, h) => a.Prepend(h), - h => MoreEnumerable.Return(h.Reverse() - .Index() + Enumerable.Empty(), + MoreEnumerable.Return, + (a, h) => a.Append(h), + h => MoreEnumerable.Return(h.Index() .ToDictionary(e => e.Value, e => e.Key)) .SelectMany(d => new[] { "a", "b", "c" }, (d, n) => d[n]) From 3706b77578148292a21326a7327f04f9eeae77c8 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Thu, 6 Aug 2020 10:49:33 +0200 Subject: [PATCH 04/32] Use terser Func expression --- MoreLinq.Test/SpillSpanTest.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/MoreLinq.Test/SpillSpanTest.cs b/MoreLinq.Test/SpillSpanTest.cs index 4841ffb12..9396fc3f2 100644 --- a/MoreLinq.Test/SpillSpanTest.cs +++ b/MoreLinq.Test/SpillSpanTest.cs @@ -22,6 +22,7 @@ namespace MoreLinq.Test using System.Text.RegularExpressions; using NUnit.Framework; using Experimental; + using static FuncModule; [TestFixture] public class SpillSpanTest @@ -52,7 +53,7 @@ from row in .ToDictionary(e => e.Value, e => e.Key)) .SelectMany(d => new[] { "a", "b", "c" }, (d, n) => d[n]) - .Select(i => new Func(s => s[i])) + .Select(i => Func((string[] s) => s[i])) .ToArray(), (bs, r) => bs.Select(b => int.Parse(b(r.Value), CultureInfo.InvariantCulture)) .Fold((a, b, c) => new { A = a, B = b, C = c })) @@ -89,7 +90,7 @@ from e in .ToDictionary(e => e.Value, e => e.Key)) .SelectMany(d => new[] { "a", "b", "c" }, (d, n) => d[n]) - .Select(i => new Func(s => s[i])) + .Select(i => Func((string[] s) => s[i])) .ToArray(), (bs, r) => new { Bindings = bs, Fields = r.Split(',') }) select e.Bindings From c639553bcaecf6293b7db5e7fbef621003afbf55 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Fri, 7 Aug 2020 17:21:22 +0200 Subject: [PATCH 05/32] Move out of experimental namespace --- MoreLinq.Test/SpillSpanTest.cs | 2 - MoreLinq/Extensions.g.cs | 111 +++++++++++++++++++++++ MoreLinq/{Experimental => }/SpillSpan.cs | 71 ++++++++++++--- 3 files changed, 167 insertions(+), 17 deletions(-) rename MoreLinq/{Experimental => }/SpillSpan.cs (60%) diff --git a/MoreLinq.Test/SpillSpanTest.cs b/MoreLinq.Test/SpillSpanTest.cs index 9396fc3f2..8ea8f0e61 100644 --- a/MoreLinq.Test/SpillSpanTest.cs +++ b/MoreLinq.Test/SpillSpanTest.cs @@ -17,11 +17,9 @@ namespace MoreLinq.Test { - using System; using System.Globalization; using System.Text.RegularExpressions; using NUnit.Framework; - using Experimental; using static FuncModule; [TestFixture] diff --git a/MoreLinq/Extensions.g.cs b/MoreLinq/Extensions.g.cs index 5e45fc65f..3448feb07 100644 --- a/MoreLinq/Extensions.g.cs +++ b/MoreLinq/Extensions.g.cs @@ -5280,6 +5280,117 @@ public static IEnumerable SortedMerge(this IEnumerableSpillSpan extension. + + [GeneratedCode("MoreLinq.ExtensionsGenerator", "1.0.0.0")] + public static partial class SpillSpanExtension + { + /// + /// TODO + /// + + public static IEnumerable<(T Head, T Item)> + SpillSpan(this IEnumerable source) => MoreEnumerable. SpillSpan(source); + + /// + /// TODO + /// + + public static IEnumerable + SpillSpan( + this IEnumerable source, + Func resultSelector) => MoreEnumerable. SpillSpan(source, resultSelector); + + /// + /// TODO + /// + + public static IEnumerable + SpillSpan( + this IEnumerable source, + Func headerSelector, + Func resultSelector) + => MoreEnumerable. SpillSpan(source, headerSelector, resultSelector); + + /// + /// TODO + /// + + public static IEnumerable + SpillSpan( + this IEnumerable source, + int count, + Func, H> headerSelector, + Func resultSelector) + => MoreEnumerable. SpillSpan(source, count, headerSelector, resultSelector); + + /// + /// TODO + /// + + public static IEnumerable + SpillSpan( + this IEnumerable source, + int count, + Func, H> headerSelector, + Func resultSelector) => MoreEnumerable. SpillSpan(source, count, headerSelector, resultSelector); + + /// + /// TODO + /// + + public static IEnumerable + SpillSpan( + this IEnumerable source, + Func predicate, + Func, H> headerSelector, + Func resultSelector) + => MoreEnumerable. SpillSpan(source, predicate, headerSelector, resultSelector); + + /// + /// TODO + /// + + public static IEnumerable + SpillSpan( + this IEnumerable source, + Func predicate, + Func, H> headerSelector, + Func resultSelector) + => MoreEnumerable. SpillSpan(source, predicate, headerSelector, resultSelector); + + /// + /// TODO + /// + + public static IEnumerable + SpillSpan( + this IEnumerable source, + Func predicate, + A empty, + Func seeder, + Func accumulator, + Func headerSelector, + Func resultSelector) + => MoreEnumerable. SpillSpan(source, predicate, empty, seeder, accumulator, headerSelector, resultSelector); + + /// + /// TODO + /// + + public static IEnumerable + SpillSpan( + this IEnumerable source, + Func chooser, + A empty, + Func seeder, + Func accumulator, + Func headerSelector, + Func resultSelector) + => MoreEnumerable. SpillSpan(source, chooser, empty, seeder, accumulator, headerSelector, resultSelector); + + } + /// Split extension. [GeneratedCode("MoreLinq.ExtensionsGenerator", "1.0.0.0")] diff --git a/MoreLinq/Experimental/SpillSpan.cs b/MoreLinq/SpillSpan.cs similarity index 60% rename from MoreLinq/Experimental/SpillSpan.cs rename to MoreLinq/SpillSpan.cs index a2a6fc7a6..72e43ff4d 100644 --- a/MoreLinq/Experimental/SpillSpan.cs +++ b/MoreLinq/SpillSpan.cs @@ -15,12 +15,12 @@ // limitations under the License. #endregion -namespace MoreLinq.Experimental +namespace MoreLinq { using System; using System.Collections.Generic; - partial class ExperimentalEnumerable + partial class MoreEnumerable { /// /// TODO @@ -48,9 +48,15 @@ public static IEnumerable SpillSpan( this IEnumerable source, Func headerSelector, - Func resultSelector) => - source.SpillSpan(1, h => headerSelector(h[0]), - (h, e, _) => resultSelector(h, e)); + Func resultSelector) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (headerSelector == null) throw new ArgumentNullException(nameof(headerSelector)); + if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + + return source.SpillSpan(1, h => headerSelector(h[0]), + (h, e, _) => resultSelector(h, e)); + } /// /// TODO @@ -61,9 +67,16 @@ public static IEnumerable this IEnumerable source, int count, Func, H> headerSelector, - Func resultSelector) => - source.SpillSpan(count, headerSelector, - (h, e, _) => resultSelector(h, e)); + Func resultSelector) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + // TODO validate count < 1? + if (headerSelector == null) throw new ArgumentNullException(nameof(headerSelector)); + if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + + return source.SpillSpan(count, headerSelector, + (h, e, _) => resultSelector(h, e)); + } /// /// TODO @@ -88,10 +101,17 @@ public static IEnumerable this IEnumerable source, Func predicate, Func, H> headerSelector, - Func resultSelector) => - source.SpillSpan((e, _) => predicate(e), - headerSelector, - (h, e, _) => resultSelector(h, e)); + Func resultSelector) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (predicate == null) throw new ArgumentNullException(nameof(predicate)); + if (headerSelector == null) throw new ArgumentNullException(nameof(headerSelector)); + if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + + return source.SpillSpan((e, _) => predicate(e), + headerSelector, + (h, e, _) => resultSelector(h, e)); + } /// /// TODO @@ -104,6 +124,11 @@ public static IEnumerable Func, H> headerSelector, Func resultSelector) { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (predicate == null) throw new ArgumentNullException(nameof(predicate)); + if (headerSelector == null) throw new ArgumentNullException(nameof(headerSelector)); + if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + return _(); IEnumerable _() { using var e = source.GetEnumerator(); @@ -133,9 +158,18 @@ public static IEnumerable Func seeder, Func accumulator, Func headerSelector, - Func resultSelector) => - source.SpillSpan(e => predicate(e) ? (true, e) : default, - empty, seeder, accumulator, headerSelector, resultSelector); + Func resultSelector) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (predicate == null) throw new ArgumentNullException(nameof(predicate)); + if (seeder == null) throw new ArgumentNullException(nameof(seeder)); + if (accumulator == null) throw new ArgumentNullException(nameof(accumulator)); + if (headerSelector == null) throw new ArgumentNullException(nameof(headerSelector)); + if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + + return source.SpillSpan(e => predicate(e) ? (true, e) : default, + empty, seeder, accumulator, headerSelector, resultSelector); + } /// /// TODO @@ -151,6 +185,13 @@ public static IEnumerable Func headerSelector, Func resultSelector) { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (chooser == null) throw new ArgumentNullException(nameof(chooser)); + if (seeder == null) throw new ArgumentNullException(nameof(seeder)); + if (accumulator == null) throw new ArgumentNullException(nameof(accumulator)); + if (headerSelector == null) throw new ArgumentNullException(nameof(headerSelector)); + if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + return _(); IEnumerable _() { using var e = source.GetEnumerator(); From c1fe89df9454d9b5e235b23a894d312fce6191f9 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Fri, 7 Aug 2020 17:37:26 +0200 Subject: [PATCH 06/32] Implement all overloads in terms of the core --- MoreLinq/SpillSpan.cs | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/MoreLinq/SpillSpan.cs b/MoreLinq/SpillSpan.cs index 72e43ff4d..e7f41027a 100644 --- a/MoreLinq/SpillSpan.cs +++ b/MoreLinq/SpillSpan.cs @@ -19,6 +19,7 @@ namespace MoreLinq { using System; using System.Collections.Generic; + using System.Linq; partial class MoreEnumerable { @@ -129,21 +130,17 @@ public static IEnumerable if (headerSelector == null) throw new ArgumentNullException(nameof(headerSelector)); if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); - return _(); IEnumerable _() - { - using var e = source.GetEnumerator(); - if (!e.MoveNext()) - yield break; - var i = 0; - var items = new List(); - for (; predicate(e.Current, i); e.MoveNext(), i++) - items.Add(e.Current); - var header = headerSelector(items); - items = null; // available for collection by GC - i = 0; - do { yield return resultSelector(header, e.Current, i++); } - while (e.MoveNext()); - } + return source.Index() + .SpillSpan(e => predicate(e.Value, e.Key), + null, + Return, + (a, h) => a.Append(h), + hs => + { + var list = hs?.Select(h => h.Value).ToList() ?? new List(); + return (Item: headerSelector(list), list.Count); + }, + (h, r) => resultSelector(h.Item, r.Value, r.Key - h.Count)); } /// From a3b2a2933bc5f7d84f0fbececf05c13e7afeb98f Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Fri, 7 Aug 2020 18:42:36 +0200 Subject: [PATCH 07/32] Have core overload provide index --- MoreLinq/Extensions.g.cs | 30 +++++++++++++++++ MoreLinq/SpillSpan.cs | 73 +++++++++++++++++++++++++++++++++------- 2 files changed, 91 insertions(+), 12 deletions(-) diff --git a/MoreLinq/Extensions.g.cs b/MoreLinq/Extensions.g.cs index 3448feb07..5988d0c7a 100644 --- a/MoreLinq/Extensions.g.cs +++ b/MoreLinq/Extensions.g.cs @@ -5378,6 +5378,21 @@ public static IEnumerable /// TODO /// + public static IEnumerable + SpillSpan( + this IEnumerable source, + Func predicate, + A empty, + Func seeder, + Func accumulator, + Func headerSelector, + Func resultSelector) + => MoreEnumerable. SpillSpan(source, predicate, empty, seeder, accumulator, headerSelector, resultSelector); + + /// + /// TODO + /// + public static IEnumerable SpillSpan( this IEnumerable source, @@ -5389,6 +5404,21 @@ public static IEnumerable Func resultSelector) => MoreEnumerable. SpillSpan(source, chooser, empty, seeder, accumulator, headerSelector, resultSelector); + /// + /// TODO + /// + + public static IEnumerable + SpillSpan( + this IEnumerable source, + Func chooser, + A empty, + Func seeder, + Func accumulator, + Func headerSelector, + Func resultSelector) + => MoreEnumerable. SpillSpan(source, chooser, empty, seeder, accumulator, headerSelector, resultSelector); + } /// Split extension. diff --git a/MoreLinq/SpillSpan.cs b/MoreLinq/SpillSpan.cs index e7f41027a..bde10cded 100644 --- a/MoreLinq/SpillSpan.cs +++ b/MoreLinq/SpillSpan.cs @@ -130,17 +130,12 @@ public static IEnumerable if (headerSelector == null) throw new ArgumentNullException(nameof(headerSelector)); if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); - return source.Index() - .SpillSpan(e => predicate(e.Value, e.Key), + return source.SpillSpan(predicate, null, Return, (a, h) => a.Append(h), - hs => - { - var list = hs?.Select(h => h.Value).ToList() ?? new List(); - return (Item: headerSelector(list), list.Count); - }, - (h, r) => resultSelector(h.Item, r.Value, r.Key - h.Count)); + hs => headerSelector(hs?.ToList() ?? new List()), + resultSelector); } /// @@ -164,7 +159,33 @@ public static IEnumerable if (headerSelector == null) throw new ArgumentNullException(nameof(headerSelector)); if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); - return source.SpillSpan(e => predicate(e) ? (true, e) : default, + return source.SpillSpan((e, _) => predicate(e), + empty, seeder, accumulator, headerSelector, + (h, e, _) => resultSelector(h, e)); + } + + /// + /// TODO + /// + + public static IEnumerable + SpillSpan( + this IEnumerable source, + Func predicate, + A empty, + Func seeder, + Func accumulator, + Func headerSelector, + Func resultSelector) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (predicate == null) throw new ArgumentNullException(nameof(predicate)); + if (seeder == null) throw new ArgumentNullException(nameof(seeder)); + if (accumulator == null) throw new ArgumentNullException(nameof(accumulator)); + if (headerSelector == null) throw new ArgumentNullException(nameof(headerSelector)); + if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + + return source.SpillSpan((e, i) => predicate(e, i) ? (true, e) : default, empty, seeder, accumulator, headerSelector, resultSelector); } @@ -189,23 +210,51 @@ public static IEnumerable if (headerSelector == null) throw new ArgumentNullException(nameof(headerSelector)); if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + return source.SpillSpan((e, _) => chooser(e), + empty, seeder, accumulator, headerSelector, + (h, e, i) => resultSelector(h, e)); + } + + /// + /// TODO + /// + + public static IEnumerable + SpillSpan( + this IEnumerable source, + Func chooser, + A empty, + Func seeder, + Func accumulator, + Func headerSelector, + Func resultSelector) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (chooser == null) throw new ArgumentNullException(nameof(chooser)); + if (seeder == null) throw new ArgumentNullException(nameof(seeder)); + if (accumulator == null) throw new ArgumentNullException(nameof(accumulator)); + if (headerSelector == null) throw new ArgumentNullException(nameof(headerSelector)); + if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + return _(); IEnumerable _() { using var e = source.GetEnumerator(); if (!e.MoveNext()) yield break; - var (span, fm) = chooser(e.Current); + var i = 0; + var (span, fm) = chooser(e.Current, i); var state = span ? seeder(fm) : empty; if (span) { if (!e.MoveNext()) yield break; - for (; chooser(e.Current) is (true, var m); e.MoveNext()) + for (; chooser(e.Current, ++i) is (true, var m); e.MoveNext()) state = accumulator(state, m); } var header = headerSelector(state); state = default; // available for collection by GC - do { yield return resultSelector(header, e.Current); } + i = 0; + do { yield return resultSelector(header, e.Current, i++); } while (e.MoveNext()); } } From 14deb60a906439e1e218e69699f75fd594b56712 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Fri, 7 Aug 2020 18:50:58 +0200 Subject: [PATCH 08/32] Fix test name --- MoreLinq.Test/SpillSpanTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MoreLinq.Test/SpillSpanTest.cs b/MoreLinq.Test/SpillSpanTest.cs index 8ea8f0e61..73b001769 100644 --- a/MoreLinq.Test/SpillSpanTest.cs +++ b/MoreLinq.Test/SpillSpanTest.cs @@ -26,7 +26,7 @@ namespace MoreLinq.Test public class SpillSpanTest { [Test] - public void Csv1() + public void Csv() { const string csv = @" a,c,b From 1fdbf8e49f8172aa96ed152311c06109c433fb88 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Fri, 7 Aug 2020 18:52:08 +0200 Subject: [PATCH 09/32] Use the simpler overload in the basic test --- MoreLinq.Test/SpillSpanTest.cs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/MoreLinq.Test/SpillSpanTest.cs b/MoreLinq.Test/SpillSpanTest.cs index 73b001769..74e61e09e 100644 --- a/MoreLinq.Test/SpillSpanTest.cs +++ b/MoreLinq.Test/SpillSpanTest.cs @@ -41,19 +41,14 @@ from line in Regex.Split(csv.Trim(), @"\r?\n") select line.Split(',').Select(f => f.Trim()).ToArray() } from row in - rows.Index() - .SpillSpan( - r => r.Key == 0, - null, - r => r.Value, - (r, _) => r, + rows.SpillSpan( h => MoreEnumerable.Return(h.Index() .ToDictionary(e => e.Value, e => e.Key)) .SelectMany(d => new[] { "a", "b", "c" }, (d, n) => d[n]) .Select(i => Func((string[] s) => s[i])) .ToArray(), - (bs, r) => bs.Select(b => int.Parse(b(r.Value), CultureInfo.InvariantCulture)) + (bs, r) => bs.Select(b => int.Parse(b(r), CultureInfo.InvariantCulture)) .Fold((a, b, c) => new { A = a, B = b, C = c })) select row; From 0d674ddc0e79206b60567a36de068597d922125d Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Fri, 7 Aug 2020 19:04:32 +0200 Subject: [PATCH 10/32] Throw "InvalidOperationException" if there's no header --- MoreLinq/SpillSpan.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MoreLinq/SpillSpan.cs b/MoreLinq/SpillSpan.cs index bde10cded..72c7d9e35 100644 --- a/MoreLinq/SpillSpan.cs +++ b/MoreLinq/SpillSpan.cs @@ -55,7 +55,7 @@ public static IEnumerable if (headerSelector == null) throw new ArgumentNullException(nameof(headerSelector)); if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); - return source.SpillSpan(1, h => headerSelector(h[0]), + return source.SpillSpan(1, h => headerSelector(h.Single()), (h, e, _) => resultSelector(h, e)); } From 26f1d96d4cbdd6a2df3e89dba1096f26fb509a98 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Fri, 7 Aug 2020 19:26:02 +0200 Subject: [PATCH 11/32] Avoid list allocation for head count == 1 --- MoreLinq/Optuple.cs | 24 ++++++++++++++++++++++++ MoreLinq/SpillSpan.cs | 8 ++++++-- 2 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 MoreLinq/Optuple.cs diff --git a/MoreLinq/Optuple.cs b/MoreLinq/Optuple.cs new file mode 100644 index 000000000..5ee5d5d85 --- /dev/null +++ b/MoreLinq/Optuple.cs @@ -0,0 +1,24 @@ +#region Copyright (c) 2018 Atif Aziz. 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 +{ + // Inspiration: https://github.com/atifaziz/Optuple + + static class Optuple + { + public static (bool, T) Some(T value) => (true, value); + } +} diff --git a/MoreLinq/SpillSpan.cs b/MoreLinq/SpillSpan.cs index 72c7d9e35..acc94d5fa 100644 --- a/MoreLinq/SpillSpan.cs +++ b/MoreLinq/SpillSpan.cs @@ -20,6 +20,7 @@ namespace MoreLinq using System; using System.Collections.Generic; using System.Linq; + using static Optuple; partial class MoreEnumerable { @@ -55,7 +56,10 @@ public static IEnumerable if (headerSelector == null) throw new ArgumentNullException(nameof(headerSelector)); if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); - return source.SpillSpan(1, h => headerSelector(h.Single()), + return source.SpillSpan((h, i) => i == 0 ? Some(h) : default, + default, Some, (a, _) => a, + h => h is (true, var v) ? headerSelector(v) + : throw new InvalidOperationException(), (h, e, _) => resultSelector(h, e)); } @@ -185,7 +189,7 @@ public static IEnumerable if (headerSelector == null) throw new ArgumentNullException(nameof(headerSelector)); if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); - return source.SpillSpan((e, i) => predicate(e, i) ? (true, e) : default, + return source.SpillSpan((e, i) => predicate(e, i) ? Some(e) : default, empty, seeder, accumulator, headerSelector, resultSelector); } From 662b3fadba0bdf9d8383954a0865860b11e3e514 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Fri, 7 Aug 2020 19:30:54 +0200 Subject: [PATCH 12/32] Remove the throw case that is impossible --- MoreLinq/Optuple.cs | 24 ------------------------ 1 file changed, 24 deletions(-) delete mode 100644 MoreLinq/Optuple.cs diff --git a/MoreLinq/Optuple.cs b/MoreLinq/Optuple.cs deleted file mode 100644 index 5ee5d5d85..000000000 --- a/MoreLinq/Optuple.cs +++ /dev/null @@ -1,24 +0,0 @@ -#region Copyright (c) 2018 Atif Aziz. 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 -{ - // Inspiration: https://github.com/atifaziz/Optuple - - static class Optuple - { - public static (bool, T) Some(T value) => (true, value); - } -} From 57d4c71ce3b35941ab54708570bf7852fe3570c7 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Fri, 7 Aug 2020 19:40:43 +0200 Subject: [PATCH 13/32] Complete previous commit --- MoreLinq/SpillSpan.cs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/MoreLinq/SpillSpan.cs b/MoreLinq/SpillSpan.cs index acc94d5fa..8801de7bf 100644 --- a/MoreLinq/SpillSpan.cs +++ b/MoreLinq/SpillSpan.cs @@ -20,7 +20,6 @@ namespace MoreLinq using System; using System.Collections.Generic; using System.Linq; - using static Optuple; partial class MoreEnumerable { @@ -56,10 +55,9 @@ public static IEnumerable if (headerSelector == null) throw new ArgumentNullException(nameof(headerSelector)); if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); - return source.SpillSpan((h, i) => i == 0 ? Some(h) : default, - default, Some, (a, _) => a, - h => h is (true, var v) ? headerSelector(v) - : throw new InvalidOperationException(), + return source.SpillSpan((_, i) => i == 0, + default, h => h, (a, _) => a, + headerSelector, (h, e, _) => resultSelector(h, e)); } @@ -189,7 +187,7 @@ public static IEnumerable if (headerSelector == null) throw new ArgumentNullException(nameof(headerSelector)); if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); - return source.SpillSpan((e, i) => predicate(e, i) ? Some(e) : default, + return source.SpillSpan((e, i) => predicate(e, i) ? (true, e) : default, empty, seeder, accumulator, headerSelector, resultSelector); } From bb3228122ea583204eba75d2ba260eedc736404d Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Fri, 7 Aug 2020 19:36:58 +0200 Subject: [PATCH 14/32] Rename to SpillHead https://github.com/morelinq/MoreLINQ/issues/752#issuecomment-670634527 --- .../{SpillSpanTest.cs => SpillHeadTest.cs} | 6 +-- MoreLinq/Extensions.g.cs | 46 +++++++++---------- MoreLinq/{SpillSpan.cs => SpillHead.cs} | 42 ++++++++--------- 3 files changed, 47 insertions(+), 47 deletions(-) rename MoreLinq.Test/{SpillSpanTest.cs => SpillHeadTest.cs} (96%) rename MoreLinq/{SpillSpan.cs => SpillHead.cs} (91%) diff --git a/MoreLinq.Test/SpillSpanTest.cs b/MoreLinq.Test/SpillHeadTest.cs similarity index 96% rename from MoreLinq.Test/SpillSpanTest.cs rename to MoreLinq.Test/SpillHeadTest.cs index 74e61e09e..94fafbb26 100644 --- a/MoreLinq.Test/SpillSpanTest.cs +++ b/MoreLinq.Test/SpillHeadTest.cs @@ -23,7 +23,7 @@ namespace MoreLinq.Test using static FuncModule; [TestFixture] - public class SpillSpanTest + public class SpillHeadTest { [Test] public void Csv() @@ -41,7 +41,7 @@ from line in Regex.Split(csv.Trim(), @"\r?\n") select line.Split(',').Select(f => f.Trim()).ToArray() } from row in - rows.SpillSpan( + rows.SpillHead( h => MoreEnumerable.Return(h.Index() .ToDictionary(e => e.Value, e => e.Key)) .SelectMany(d => new[] { "a", "b", "c" }, @@ -75,7 +75,7 @@ public void CsvWithColumnsInCommentLines() from e in Regex.Split(csv.Trim(), @"\r?\n") .Select(line => line.Trim()) - .SpillSpan(h => Regex.Match(h, @"^;\s*(\w+)") is var m & m.Success ? (true, m.Groups[1].Value) : default, + .SpillHead(h => Regex.Match(h, @"^;\s*(\w+)") is var m & m.Success ? (true, m.Groups[1].Value) : default, Enumerable.Empty(), MoreEnumerable.Return, (a, h) => a.Append(h), diff --git a/MoreLinq/Extensions.g.cs b/MoreLinq/Extensions.g.cs index 5988d0c7a..1f063f33f 100644 --- a/MoreLinq/Extensions.g.cs +++ b/MoreLinq/Extensions.g.cs @@ -5280,91 +5280,91 @@ public static IEnumerable SortedMerge(this IEnumerableSpillSpan extension. + /// SpillHead extension. [GeneratedCode("MoreLinq.ExtensionsGenerator", "1.0.0.0")] - public static partial class SpillSpanExtension + public static partial class SpillHeadExtension { /// /// TODO /// public static IEnumerable<(T Head, T Item)> - SpillSpan(this IEnumerable source) => MoreEnumerable. SpillSpan(source); + SpillHead(this IEnumerable source) => MoreEnumerable. SpillHead(source); /// /// TODO /// public static IEnumerable - SpillSpan( + SpillHead( this IEnumerable source, - Func resultSelector) => MoreEnumerable. SpillSpan(source, resultSelector); + Func resultSelector) => MoreEnumerable. SpillHead(source, resultSelector); /// /// TODO /// public static IEnumerable - SpillSpan( + SpillHead( this IEnumerable source, Func headerSelector, Func resultSelector) - => MoreEnumerable. SpillSpan(source, headerSelector, resultSelector); + => MoreEnumerable. SpillHead(source, headerSelector, resultSelector); /// /// TODO /// public static IEnumerable - SpillSpan( + SpillHead( this IEnumerable source, int count, Func, H> headerSelector, Func resultSelector) - => MoreEnumerable. SpillSpan(source, count, headerSelector, resultSelector); + => MoreEnumerable. SpillHead(source, count, headerSelector, resultSelector); /// /// TODO /// public static IEnumerable - SpillSpan( + SpillHead( this IEnumerable source, int count, Func, H> headerSelector, - Func resultSelector) => MoreEnumerable. SpillSpan(source, count, headerSelector, resultSelector); + Func resultSelector) => MoreEnumerable. SpillHead(source, count, headerSelector, resultSelector); /// /// TODO /// public static IEnumerable - SpillSpan( + SpillHead( this IEnumerable source, Func predicate, Func, H> headerSelector, Func resultSelector) - => MoreEnumerable. SpillSpan(source, predicate, headerSelector, resultSelector); + => MoreEnumerable. SpillHead(source, predicate, headerSelector, resultSelector); /// /// TODO /// public static IEnumerable - SpillSpan( + SpillHead( this IEnumerable source, Func predicate, Func, H> headerSelector, Func resultSelector) - => MoreEnumerable. SpillSpan(source, predicate, headerSelector, resultSelector); + => MoreEnumerable. SpillHead(source, predicate, headerSelector, resultSelector); /// /// TODO /// public static IEnumerable - SpillSpan( + SpillHead( this IEnumerable source, Func predicate, A empty, @@ -5372,14 +5372,14 @@ public static IEnumerable Func accumulator, Func headerSelector, Func resultSelector) - => MoreEnumerable. SpillSpan(source, predicate, empty, seeder, accumulator, headerSelector, resultSelector); + => MoreEnumerable. SpillHead(source, predicate, empty, seeder, accumulator, headerSelector, resultSelector); /// /// TODO /// public static IEnumerable - SpillSpan( + SpillHead( this IEnumerable source, Func predicate, A empty, @@ -5387,14 +5387,14 @@ public static IEnumerable Func accumulator, Func headerSelector, Func resultSelector) - => MoreEnumerable. SpillSpan(source, predicate, empty, seeder, accumulator, headerSelector, resultSelector); + => MoreEnumerable. SpillHead(source, predicate, empty, seeder, accumulator, headerSelector, resultSelector); /// /// TODO /// public static IEnumerable - SpillSpan( + SpillHead( this IEnumerable source, Func chooser, A empty, @@ -5402,14 +5402,14 @@ public static IEnumerable Func accumulator, Func headerSelector, Func resultSelector) - => MoreEnumerable. SpillSpan(source, chooser, empty, seeder, accumulator, headerSelector, resultSelector); + => MoreEnumerable. SpillHead(source, chooser, empty, seeder, accumulator, headerSelector, resultSelector); /// /// TODO /// public static IEnumerable - SpillSpan( + SpillHead( this IEnumerable source, Func chooser, A empty, @@ -5417,7 +5417,7 @@ public static IEnumerable Func accumulator, Func headerSelector, Func resultSelector) - => MoreEnumerable. SpillSpan(source, chooser, empty, seeder, accumulator, headerSelector, resultSelector); + => MoreEnumerable. SpillHead(source, chooser, empty, seeder, accumulator, headerSelector, resultSelector); } diff --git a/MoreLinq/SpillSpan.cs b/MoreLinq/SpillHead.cs similarity index 91% rename from MoreLinq/SpillSpan.cs rename to MoreLinq/SpillHead.cs index 8801de7bf..21c9297f1 100644 --- a/MoreLinq/SpillSpan.cs +++ b/MoreLinq/SpillHead.cs @@ -28,25 +28,25 @@ partial class MoreEnumerable /// public static IEnumerable<(T Head, T Item)> - SpillSpan(this IEnumerable source) => - source.SpillSpan(h => h, ValueTuple.Create); + SpillHead(this IEnumerable source) => + source.SpillHead(h => h, ValueTuple.Create); /// /// TODO /// public static IEnumerable - SpillSpan( + SpillHead( this IEnumerable source, Func resultSelector) => - source.SpillSpan(h => h, resultSelector); + source.SpillHead(h => h, resultSelector); /// /// TODO /// public static IEnumerable - SpillSpan( + SpillHead( this IEnumerable source, Func headerSelector, Func resultSelector) @@ -55,7 +55,7 @@ public static IEnumerable if (headerSelector == null) throw new ArgumentNullException(nameof(headerSelector)); if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); - return source.SpillSpan((_, i) => i == 0, + return source.SpillHead((_, i) => i == 0, default, h => h, (a, _) => a, headerSelector, (h, e, _) => resultSelector(h, e)); @@ -66,7 +66,7 @@ public static IEnumerable /// public static IEnumerable - SpillSpan( + SpillHead( this IEnumerable source, int count, Func, H> headerSelector, @@ -77,7 +77,7 @@ public static IEnumerable if (headerSelector == null) throw new ArgumentNullException(nameof(headerSelector)); if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); - return source.SpillSpan(count, headerSelector, + return source.SpillHead(count, headerSelector, (h, e, _) => resultSelector(h, e)); } @@ -86,12 +86,12 @@ public static IEnumerable /// public static IEnumerable - SpillSpan( + SpillHead( this IEnumerable source, int count, Func, H> headerSelector, Func resultSelector) => - source.SpillSpan((e, i) => i < count, + source.SpillHead((e, i) => i < count, headerSelector, resultSelector); @@ -100,7 +100,7 @@ public static IEnumerable /// public static IEnumerable - SpillSpan( + SpillHead( this IEnumerable source, Func predicate, Func, H> headerSelector, @@ -111,7 +111,7 @@ public static IEnumerable if (headerSelector == null) throw new ArgumentNullException(nameof(headerSelector)); if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); - return source.SpillSpan((e, _) => predicate(e), + return source.SpillHead((e, _) => predicate(e), headerSelector, (h, e, _) => resultSelector(h, e)); } @@ -121,7 +121,7 @@ public static IEnumerable /// public static IEnumerable - SpillSpan( + SpillHead( this IEnumerable source, Func predicate, Func, H> headerSelector, @@ -132,7 +132,7 @@ public static IEnumerable if (headerSelector == null) throw new ArgumentNullException(nameof(headerSelector)); if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); - return source.SpillSpan(predicate, + return source.SpillHead(predicate, null, Return, (a, h) => a.Append(h), @@ -145,7 +145,7 @@ public static IEnumerable /// public static IEnumerable - SpillSpan( + SpillHead( this IEnumerable source, Func predicate, A empty, @@ -161,7 +161,7 @@ public static IEnumerable if (headerSelector == null) throw new ArgumentNullException(nameof(headerSelector)); if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); - return source.SpillSpan((e, _) => predicate(e), + return source.SpillHead((e, _) => predicate(e), empty, seeder, accumulator, headerSelector, (h, e, _) => resultSelector(h, e)); } @@ -171,7 +171,7 @@ public static IEnumerable /// public static IEnumerable - SpillSpan( + SpillHead( this IEnumerable source, Func predicate, A empty, @@ -187,7 +187,7 @@ public static IEnumerable if (headerSelector == null) throw new ArgumentNullException(nameof(headerSelector)); if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); - return source.SpillSpan((e, i) => predicate(e, i) ? (true, e) : default, + return source.SpillHead((e, i) => predicate(e, i) ? (true, e) : default, empty, seeder, accumulator, headerSelector, resultSelector); } @@ -196,7 +196,7 @@ public static IEnumerable /// public static IEnumerable - SpillSpan( + SpillHead( this IEnumerable source, Func chooser, A empty, @@ -212,7 +212,7 @@ public static IEnumerable if (headerSelector == null) throw new ArgumentNullException(nameof(headerSelector)); if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); - return source.SpillSpan((e, _) => chooser(e), + return source.SpillHead((e, _) => chooser(e), empty, seeder, accumulator, headerSelector, (h, e, i) => resultSelector(h, e)); } @@ -222,7 +222,7 @@ public static IEnumerable /// public static IEnumerable - SpillSpan( + SpillHead( this IEnumerable source, Func chooser, A empty, From 4c38d38cf491542442f5389f1de623f5350e8fc2 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Mon, 10 Aug 2020 18:22:00 +0200 Subject: [PATCH 15/32] Save final list copy --- MoreLinq/SpillHead.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/MoreLinq/SpillHead.cs b/MoreLinq/SpillHead.cs index 21c9297f1..4975f52d3 100644 --- a/MoreLinq/SpillHead.cs +++ b/MoreLinq/SpillHead.cs @@ -19,7 +19,6 @@ namespace MoreLinq { using System; using System.Collections.Generic; - using System.Linq; partial class MoreEnumerable { @@ -134,9 +133,9 @@ public static IEnumerable return source.SpillHead(predicate, null, - Return, - (a, h) => a.Append(h), - hs => headerSelector(hs?.ToList() ?? new List()), + h => new List(), + (a, h) => { a.Add(h); return a; }, + hs => headerSelector(hs ?? new List()), resultSelector); } From 2582f4a0d879d76d02da22be032a7562f8f5a473 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Mon, 10 Aug 2020 18:28:40 +0200 Subject: [PATCH 16/32] Add complementary overloads --- MoreLinq/SpillHead.cs | 48 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/MoreLinq/SpillHead.cs b/MoreLinq/SpillHead.cs index 4975f52d3..1923b5f33 100644 --- a/MoreLinq/SpillHead.cs +++ b/MoreLinq/SpillHead.cs @@ -131,11 +131,53 @@ public static IEnumerable if (headerSelector == null) throw new ArgumentNullException(nameof(headerSelector)); if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); - return source.SpillHead(predicate, + return source.SpillHead((e, i) => predicate(e, i) ? (true, e) : default, + headerSelector, + resultSelector); + } + + /// + /// TODO + /// + + public static IEnumerable + SpillHead( + this IEnumerable source, + Func chooser, + Func, H> headerSelector, + Func resultSelector) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (chooser == null) throw new ArgumentNullException(nameof(chooser)); + if (headerSelector == null) throw new ArgumentNullException(nameof(headerSelector)); + if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + + return source.SpillHead((e, _) => chooser(e), + headerSelector, + (h, e, _) => resultSelector(h, e)); + } + + /// + /// TODO + /// + + public static IEnumerable + SpillHead( + this IEnumerable source, + Func chooser, + Func, H> headerSelector, + Func resultSelector) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (chooser == null) throw new ArgumentNullException(nameof(chooser)); + if (headerSelector == null) throw new ArgumentNullException(nameof(headerSelector)); + if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + + return source.SpillHead(chooser, null, - h => new List(), + h => new List(), (a, h) => { a.Add(h); return a; }, - hs => headerSelector(hs ?? new List()), + hs => headerSelector(hs ?? new List()), resultSelector); } From 3b7397fdca76b43c2258ac639e37e1b31e9f455b Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Mon, 10 Aug 2020 18:34:37 +0200 Subject: [PATCH 17/32] Expand generic type parameter names --- MoreLinq/Extensions.g.cs | 130 ++++++++++++++++++++++---------------- MoreLinq/SpillHead.cs | 132 +++++++++++++++++++-------------------- 2 files changed, 143 insertions(+), 119 deletions(-) diff --git a/MoreLinq/Extensions.g.cs b/MoreLinq/Extensions.g.cs index 1f063f33f..e9af12074 100644 --- a/MoreLinq/Extensions.g.cs +++ b/MoreLinq/Extensions.g.cs @@ -5296,127 +5296,151 @@ public static partial class SpillHeadExtension /// TODO /// - public static IEnumerable - SpillHead( + public static IEnumerable + SpillHead( this IEnumerable source, - Func resultSelector) => MoreEnumerable. SpillHead(source, resultSelector); + Func resultSelector) => MoreEnumerable. SpillHead(source, resultSelector); /// /// TODO /// - public static IEnumerable - SpillHead( + public static IEnumerable + SpillHead( this IEnumerable source, - Func headerSelector, - Func resultSelector) + Func headerSelector, + Func resultSelector) => MoreEnumerable. SpillHead(source, headerSelector, resultSelector); /// /// TODO /// - public static IEnumerable - SpillHead( + public static IEnumerable + SpillHead( this IEnumerable source, int count, - Func, H> headerSelector, - Func resultSelector) + Func, THead> headerSelector, + Func resultSelector) => MoreEnumerable. SpillHead(source, count, headerSelector, resultSelector); /// /// TODO /// - public static IEnumerable - SpillHead( + public static IEnumerable + SpillHead( this IEnumerable source, int count, - Func, H> headerSelector, - Func resultSelector) => MoreEnumerable. SpillHead(source, count, headerSelector, resultSelector); + Func, THead> headerSelector, + Func resultSelector) => MoreEnumerable. SpillHead(source, count, headerSelector, resultSelector); /// /// TODO /// - public static IEnumerable - SpillHead( + public static IEnumerable + SpillHead( this IEnumerable source, Func predicate, - Func, H> headerSelector, - Func resultSelector) + Func, THead> headerSelector, + Func resultSelector) => MoreEnumerable. SpillHead(source, predicate, headerSelector, resultSelector); /// /// TODO /// - public static IEnumerable - SpillHead( + public static IEnumerable + SpillHead( this IEnumerable source, Func predicate, - Func, H> headerSelector, - Func resultSelector) + Func, THead> headerSelector, + Func resultSelector) => MoreEnumerable. SpillHead(source, predicate, headerSelector, resultSelector); /// /// TODO /// - public static IEnumerable - SpillHead( + public static IEnumerable + SpillHead( + this IEnumerable source, + Func chooser, + Func, THead> headerSelector, + Func resultSelector) + => MoreEnumerable. SpillHead(source, chooser, headerSelector, resultSelector); + + /// + /// TODO + /// + + public static IEnumerable + SpillHead( + this IEnumerable source, + Func chooser, + Func, THead> headerSelector, + Func resultSelector) + => MoreEnumerable. SpillHead(source, chooser, headerSelector, resultSelector); + + /// + /// TODO + /// + + public static IEnumerable + SpillHead( this IEnumerable source, Func predicate, - A empty, - Func seeder, - Func accumulator, - Func headerSelector, - Func resultSelector) + TState empty, + Func seeder, + Func accumulator, + Func headerSelector, + Func resultSelector) => MoreEnumerable. SpillHead(source, predicate, empty, seeder, accumulator, headerSelector, resultSelector); /// /// TODO /// - public static IEnumerable - SpillHead( + public static IEnumerable + SpillHead( this IEnumerable source, Func predicate, - A empty, - Func seeder, - Func accumulator, - Func headerSelector, - Func resultSelector) + TState empty, + Func seeder, + Func accumulator, + Func headerSelector, + Func resultSelector) => MoreEnumerable. SpillHead(source, predicate, empty, seeder, accumulator, headerSelector, resultSelector); /// /// TODO /// - public static IEnumerable - SpillHead( + public static IEnumerable + SpillHead( this IEnumerable source, - Func chooser, - A empty, - Func seeder, - Func accumulator, - Func headerSelector, - Func resultSelector) + Func chooser, + TState empty, + Func seeder, + Func accumulator, + Func headerSelector, + Func resultSelector) => MoreEnumerable. SpillHead(source, chooser, empty, seeder, accumulator, headerSelector, resultSelector); /// /// TODO /// - public static IEnumerable - SpillHead( + public static IEnumerable + SpillHead( this IEnumerable source, - Func chooser, - A empty, - Func seeder, - Func accumulator, - Func headerSelector, - Func resultSelector) + Func chooser, + TState empty, + Func seeder, + Func accumulator, + Func headerSelector, + Func resultSelector) => MoreEnumerable. SpillHead(source, chooser, empty, seeder, accumulator, headerSelector, resultSelector); } diff --git a/MoreLinq/SpillHead.cs b/MoreLinq/SpillHead.cs index 1923b5f33..14301ea17 100644 --- a/MoreLinq/SpillHead.cs +++ b/MoreLinq/SpillHead.cs @@ -34,21 +34,21 @@ partial class MoreEnumerable /// TODO /// - public static IEnumerable - SpillHead( + public static IEnumerable + SpillHead( this IEnumerable source, - Func resultSelector) => + Func resultSelector) => source.SpillHead(h => h, resultSelector); /// /// TODO /// - public static IEnumerable - SpillHead( + public static IEnumerable + SpillHead( this IEnumerable source, - Func headerSelector, - Func resultSelector) + Func headerSelector, + Func resultSelector) { if (source == null) throw new ArgumentNullException(nameof(source)); if (headerSelector == null) throw new ArgumentNullException(nameof(headerSelector)); @@ -64,12 +64,12 @@ public static IEnumerable /// TODO /// - public static IEnumerable - SpillHead( + public static IEnumerable + SpillHead( this IEnumerable source, int count, - Func, H> headerSelector, - Func resultSelector) + Func, THead> headerSelector, + Func resultSelector) { if (source == null) throw new ArgumentNullException(nameof(source)); // TODO validate count < 1? @@ -84,12 +84,12 @@ public static IEnumerable /// TODO /// - public static IEnumerable - SpillHead( + public static IEnumerable + SpillHead( this IEnumerable source, int count, - Func, H> headerSelector, - Func resultSelector) => + Func, THead> headerSelector, + Func resultSelector) => source.SpillHead((e, i) => i < count, headerSelector, resultSelector); @@ -98,12 +98,12 @@ public static IEnumerable /// TODO /// - public static IEnumerable - SpillHead( + public static IEnumerable + SpillHead( this IEnumerable source, Func predicate, - Func, H> headerSelector, - Func resultSelector) + Func, THead> headerSelector, + Func resultSelector) { if (source == null) throw new ArgumentNullException(nameof(source)); if (predicate == null) throw new ArgumentNullException(nameof(predicate)); @@ -119,12 +119,12 @@ public static IEnumerable /// TODO /// - public static IEnumerable - SpillHead( + public static IEnumerable + SpillHead( this IEnumerable source, Func predicate, - Func, H> headerSelector, - Func resultSelector) + Func, THead> headerSelector, + Func resultSelector) { if (source == null) throw new ArgumentNullException(nameof(source)); if (predicate == null) throw new ArgumentNullException(nameof(predicate)); @@ -140,12 +140,12 @@ public static IEnumerable /// TODO /// - public static IEnumerable - SpillHead( + public static IEnumerable + SpillHead( this IEnumerable source, - Func chooser, - Func, H> headerSelector, - Func resultSelector) + Func chooser, + Func, THead> headerSelector, + Func resultSelector) { if (source == null) throw new ArgumentNullException(nameof(source)); if (chooser == null) throw new ArgumentNullException(nameof(chooser)); @@ -161,12 +161,12 @@ public static IEnumerable /// TODO /// - public static IEnumerable - SpillHead( + public static IEnumerable + SpillHead( this IEnumerable source, - Func chooser, - Func, H> headerSelector, - Func resultSelector) + Func chooser, + Func, THead> headerSelector, + Func resultSelector) { if (source == null) throw new ArgumentNullException(nameof(source)); if (chooser == null) throw new ArgumentNullException(nameof(chooser)); @@ -175,9 +175,9 @@ public static IEnumerable return source.SpillHead(chooser, null, - h => new List(), + h => new List(), (a, h) => { a.Add(h); return a; }, - hs => headerSelector(hs ?? new List()), + hs => headerSelector(hs ?? new List()), resultSelector); } @@ -185,15 +185,15 @@ public static IEnumerable /// TODO /// - public static IEnumerable - SpillHead( + public static IEnumerable + SpillHead( this IEnumerable source, Func predicate, - A empty, - Func seeder, - Func accumulator, - Func headerSelector, - Func resultSelector) + TState empty, + Func seeder, + Func accumulator, + Func headerSelector, + Func resultSelector) { if (source == null) throw new ArgumentNullException(nameof(source)); if (predicate == null) throw new ArgumentNullException(nameof(predicate)); @@ -211,15 +211,15 @@ public static IEnumerable /// TODO /// - public static IEnumerable - SpillHead( + public static IEnumerable + SpillHead( this IEnumerable source, Func predicate, - A empty, - Func seeder, - Func accumulator, - Func headerSelector, - Func resultSelector) + TState empty, + Func seeder, + Func accumulator, + Func headerSelector, + Func resultSelector) { if (source == null) throw new ArgumentNullException(nameof(source)); if (predicate == null) throw new ArgumentNullException(nameof(predicate)); @@ -236,15 +236,15 @@ public static IEnumerable /// TODO /// - public static IEnumerable - SpillHead( + public static IEnumerable + SpillHead( this IEnumerable source, - Func chooser, - A empty, - Func seeder, - Func accumulator, - Func headerSelector, - Func resultSelector) + Func chooser, + TState empty, + Func seeder, + Func accumulator, + Func headerSelector, + Func resultSelector) { if (source == null) throw new ArgumentNullException(nameof(source)); if (chooser == null) throw new ArgumentNullException(nameof(chooser)); @@ -262,15 +262,15 @@ public static IEnumerable /// TODO /// - public static IEnumerable - SpillHead( + public static IEnumerable + SpillHead( this IEnumerable source, - Func chooser, - A empty, - Func seeder, - Func accumulator, - Func headerSelector, - Func resultSelector) + Func chooser, + TState empty, + Func seeder, + Func accumulator, + Func headerSelector, + Func resultSelector) { if (source == null) throw new ArgumentNullException(nameof(source)); if (chooser == null) throw new ArgumentNullException(nameof(chooser)); @@ -279,7 +279,7 @@ public static IEnumerable if (headerSelector == null) throw new ArgumentNullException(nameof(headerSelector)); if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); - return _(); IEnumerable _() + return _(); IEnumerable _() { using var e = source.GetEnumerator(); if (!e.MoveNext()) From 9820013e7b301c6e5b38a966e11f1624f50450d2 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Mon, 10 Aug 2020 18:49:59 +0200 Subject: [PATCH 18/32] Review code layout --- MoreLinq/SpillHead.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/MoreLinq/SpillHead.cs b/MoreLinq/SpillHead.cs index 14301ea17..82b69fba2 100644 --- a/MoreLinq/SpillHead.cs +++ b/MoreLinq/SpillHead.cs @@ -91,8 +91,7 @@ public static IEnumerable Func, THead> headerSelector, Func resultSelector) => source.SpillHead((e, i) => i < count, - headerSelector, - resultSelector); + headerSelector, resultSelector); /// /// TODO @@ -132,8 +131,7 @@ public static IEnumerable if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); return source.SpillHead((e, i) => predicate(e, i) ? (true, e) : default, - headerSelector, - resultSelector); + headerSelector, resultSelector); } /// From f56c76ff3dbf278fc0e6c45b824f26506f0bb679 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Mon, 10 Aug 2020 18:50:43 +0200 Subject: [PATCH 19/32] Discard unused args --- MoreLinq/SpillHead.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MoreLinq/SpillHead.cs b/MoreLinq/SpillHead.cs index 82b69fba2..2f54976a2 100644 --- a/MoreLinq/SpillHead.cs +++ b/MoreLinq/SpillHead.cs @@ -90,7 +90,7 @@ public static IEnumerable int count, Func, THead> headerSelector, Func resultSelector) => - source.SpillHead((e, i) => i < count, + source.SpillHead((_, i) => i < count, headerSelector, resultSelector); /// @@ -253,7 +253,7 @@ public static IEnumerable return source.SpillHead((e, _) => chooser(e), empty, seeder, accumulator, headerSelector, - (h, e, i) => resultSelector(h, e)); + (h, e, _) => resultSelector(h, e)); } /// From 368e5cc45cfd2424c54073c1c0139af15b717fd0 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Mon, 10 Aug 2020 18:53:15 +0200 Subject: [PATCH 20/32] Add test for simplest case --- MoreLinq.Test/SpillHeadTest.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/MoreLinq.Test/SpillHeadTest.cs b/MoreLinq.Test/SpillHeadTest.cs index 94fafbb26..42218c4a2 100644 --- a/MoreLinq.Test/SpillHeadTest.cs +++ b/MoreLinq.Test/SpillHeadTest.cs @@ -25,6 +25,17 @@ namespace MoreLinq.Test [TestFixture] public class SpillHeadTest { + [Test] + public void RepeatHeadElementWithRest() + { + var result = Enumerable.Range(5, 6).SpillHead(); + + Assert.That(result, Is.EqualTo(new[] + { + (5, 6), (5, 7), (5, 8), (5, 9), (5, 10) + })); + } + [Test] public void Csv() { From fbfe12a2de327992d2d46273813bfca2b08ff682 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Mon, 10 Aug 2020 18:54:29 +0200 Subject: [PATCH 21/32] Fix initial head element missing in list --- MoreLinq.Test/SpillHeadTest.cs | 13 +++++++++++++ MoreLinq/SpillHead.cs | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/MoreLinq.Test/SpillHeadTest.cs b/MoreLinq.Test/SpillHeadTest.cs index 42218c4a2..2b7535419 100644 --- a/MoreLinq.Test/SpillHeadTest.cs +++ b/MoreLinq.Test/SpillHeadTest.cs @@ -36,6 +36,19 @@ public void RepeatHeadElementWithRest() })); } + [Test] + public void RepeatHeadElementsWithRest() + { + var result = + Enumerable.Range(5, 6) + .SpillHead(2, hs => hs, (h, d) => (h[0], h[1], d)); + + Assert.That(result, Is.EqualTo(new[] + { + (5, 6, 7), (5, 6, 8), (5, 6, 9), (5, 6, 10) + })); + } + [Test] public void Csv() { diff --git a/MoreLinq/SpillHead.cs b/MoreLinq/SpillHead.cs index 2f54976a2..6375aae34 100644 --- a/MoreLinq/SpillHead.cs +++ b/MoreLinq/SpillHead.cs @@ -173,7 +173,7 @@ public static IEnumerable return source.SpillHead(chooser, null, - h => new List(), + h => new List { h }, (a, h) => { a.Add(h); return a; }, hs => headerSelector(hs ?? new List()), resultSelector); From 1ec6e4418966dd78f53eac917ed0e9f90c6bf787 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Mon, 10 Aug 2020 19:07:55 +0200 Subject: [PATCH 22/32] List in package description --- MoreLinq/MoreLinq.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/MoreLinq/MoreLinq.csproj b/MoreLinq/MoreLinq.csproj index 22f0c7434..51d03e04d 100644 --- a/MoreLinq/MoreLinq.csproj +++ b/MoreLinq/MoreLinq.csproj @@ -87,6 +87,7 @@ - SkipUntil - Slice - SortedMerge + - SpillHead - Split - StartsWith - Subsets From c61f8e0d7f21c7554e6a658bbf1eb92dd17fbaf3 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Mon, 10 Aug 2020 19:08:11 +0200 Subject: [PATCH 23/32] Add to-do in README doc --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 03631fdef..9dab6fb94 100644 --- a/README.md +++ b/README.md @@ -571,6 +571,13 @@ descending) into a single sequence that preserves that order. This method has 2 overloads. +### SpillHead + +TODO + +This method has 13 overloads. + + ### Split Splits the source sequence by a separator. From 9906ca1aed5daca8a887fb5026e7ef70095dad28 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Tue, 11 Aug 2020 10:08:11 +0200 Subject: [PATCH 24/32] Add test case for when there is just head --- MoreLinq.Test/SpillHeadTest.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/MoreLinq.Test/SpillHeadTest.cs b/MoreLinq.Test/SpillHeadTest.cs index 2b7535419..f495ef572 100644 --- a/MoreLinq.Test/SpillHeadTest.cs +++ b/MoreLinq.Test/SpillHeadTest.cs @@ -36,6 +36,13 @@ public void RepeatHeadElementWithRest() })); } + [Test] + public void HeadElementOnly() + { + var result = new[] { "head" }.SpillHead(); + Assert.That(result, Is.Empty); + } + [Test] public void RepeatHeadElementsWithRest() { From ebc6b1f1909a509f7ff5b212341b41bb2d29043d Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Tue, 11 Aug 2020 10:28:40 +0200 Subject: [PATCH 25/32] Fix head loop bug --- MoreLinq.Test/SpillHeadTest.cs | 15 +++++++++++++++ MoreLinq/SpillHead.cs | 13 +++++++++---- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/MoreLinq.Test/SpillHeadTest.cs b/MoreLinq.Test/SpillHeadTest.cs index f495ef572..e24ef440b 100644 --- a/MoreLinq.Test/SpillHeadTest.cs +++ b/MoreLinq.Test/SpillHeadTest.cs @@ -17,6 +17,7 @@ namespace MoreLinq.Test { + using System.Collections.Generic; using System.Globalization; using System.Text.RegularExpressions; using NUnit.Framework; @@ -56,6 +57,20 @@ public void RepeatHeadElementsWithRest() })); } + [TestCase(0)] + [TestCase(1)] + [TestCase(2)] + [TestCase(3)] + public void InsufficientElementsPerHeadCount(int count) + { + var result = Enumerable.Repeat("head", count) + .SpillHead(3, + BreakingFunc.Of, object>(), + BreakingFunc.Of()); + + Assert.That(result, Is.Empty); + } + [Test] public void Csv() { diff --git a/MoreLinq/SpillHead.cs b/MoreLinq/SpillHead.cs index 6375aae34..52c6f393b 100644 --- a/MoreLinq/SpillHead.cs +++ b/MoreLinq/SpillHead.cs @@ -287,10 +287,15 @@ public static IEnumerable var state = span ? seeder(fm) : empty; if (span) { - if (!e.MoveNext()) - yield break; - for (; chooser(e.Current, ++i) is (true, var m); e.MoveNext()) - state = accumulator(state, m); + for (;;) + { + if (!e.MoveNext()) + yield break; + if (chooser(e.Current, ++i) is (true, var m)) + state = accumulator(state, m); + else + break; + } } var header = headerSelector(state); state = default; // available for collection by GC From af3ad950450e08ebca7945d7e7849129cde0ab1f Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Tue, 11 Aug 2020 12:35:25 +0200 Subject: [PATCH 26/32] Test for when none satisfy head predicate --- MoreLinq.Test/SpillHeadTest.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/MoreLinq.Test/SpillHeadTest.cs b/MoreLinq.Test/SpillHeadTest.cs index e24ef440b..d56e73122 100644 --- a/MoreLinq.Test/SpillHeadTest.cs +++ b/MoreLinq.Test/SpillHeadTest.cs @@ -71,6 +71,22 @@ public void InsufficientElementsPerHeadCount(int count) Assert.That(result, Is.Empty); } + [Test] + public void NoneSatisfyHeadPredicate() + { + var words = new[] { "foo", "bar", "baz" }; + var result = words.SpillHead(e => e == "head", + hs => hs.Count, + (hc, e) => new { HeadCount = 0, Data = e }); + + Assert.That(result, Is.EqualTo(new[] + { + new { HeadCount = 0, Data = "foo" }, + new { HeadCount = 0, Data = "bar" }, + new { HeadCount = 0, Data = "baz" }, + })); + } + [Test] public void Csv() { From 6c8058615193656841bbaddd36f8adcc0d63d0d0 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Tue, 11 Aug 2020 13:06:28 +0200 Subject: [PATCH 27/32] Complete documentation --- MoreLinq/SpillHead.cs | 355 ++++++++++++++++++++++++++++++++++++++---- README.md | 8 +- 2 files changed, 336 insertions(+), 27 deletions(-) diff --git a/MoreLinq/SpillHead.cs b/MoreLinq/SpillHead.cs index 52c6f393b..de13ddcff 100644 --- a/MoreLinq/SpillHead.cs +++ b/MoreLinq/SpillHead.cs @@ -23,16 +23,39 @@ namespace MoreLinq partial class MoreEnumerable { /// - /// TODO + /// Couples the first/head element of the sequence exclusively with + /// the remainder elements of the sequence. /// + /// Type of source sequence elements. + /// The source sequence. + /// + /// A sequence with the head element coupled together with the + /// remainder elements of the source sequence. + /// + /// This operator uses deferred execution and streams its results. + /// public static IEnumerable<(T Head, T Item)> SpillHead(this IEnumerable source) => source.SpillHead(h => h, ValueTuple.Create); /// - /// TODO + /// Projects the first/head element of the sequence exclusively with + /// the remainder elements of the sequence. An additional argument + /// specifies a function that receives an element of sequence + /// together with the first/head element and returns a projection. /// + /// Type of source sequence elements. + /// + /// Type of elements of the returned sequence. + /// The source sequence. + /// Function that projects a result + /// given the head and one of the remainder elements. + /// A sequence of elements returned by + /// . + /// + /// This operator uses deferred execution and streams its results. + /// public static IEnumerable SpillHead( @@ -41,8 +64,28 @@ public static IEnumerable source.SpillHead(h => h, resultSelector); /// - /// TODO + /// Projects the first/head element of the sequence exclusively with + /// the remainder elements of the sequence. Additional arguments + /// specify functions to project the head as well as the elements + /// of the resulting sequence. /// + /// Type of source sequence elements. + /// Type of head projection. + /// + /// Type of elements of the returned sequence. + /// The source sequence. + /// Function that projects an + /// intermediate head representation. + /// + /// Function that projects a result + /// given the head projection returned by + /// and one of the remainder elements. + /// + /// A sequence of elements returned by + /// . + /// + /// This operator uses deferred execution and streams its results. + /// public static IEnumerable SpillHead( @@ -61,8 +104,30 @@ public static IEnumerable } /// - /// TODO + /// Projects the head elements of the sequence exclusively with the + /// remainder elements of the sequence. Additional arguments specify + /// functions to project the head as well as the elements of the + /// resulting sequence. /// + /// Type of source sequence elements. + /// Type of head projection. + /// + /// Type of elements of the returned sequence. + /// The source sequence. + /// Count head elements to collect. + /// Function that projects an + /// intermediate head representation given a list of exactly + /// head elements. + /// + /// Function that projects a result + /// given the head projection returned by + /// and one of the remainder elements. + /// + /// A sequence of elements returned by + /// . + /// + /// This operator uses deferred execution and streams its results. + /// public static IEnumerable SpillHead( @@ -81,8 +146,31 @@ public static IEnumerable } /// - /// TODO + /// Projects the head elements of the sequence exclusively with the + /// remainder elements of the sequence. Additional arguments specify + /// functions to project the head as well as the elements (along with + /// their index) of the resulting sequence. /// + /// Type of source sequence elements. + /// Type of head projection. + /// + /// Type of elements of the returned sequence. + /// The source sequence. + /// Count head elements to collect. + /// Function that projects an + /// intermediate head representation given a list of exactly + /// head elements. + /// + /// Function that projects a result given + /// the head projection returned by + /// and one of the remainder elements along with its zero-based index + /// (where zero is the first non-head element). + /// + /// A sequence of elements returned by + /// . + /// + /// This operator uses deferred execution and streams its results. + /// public static IEnumerable SpillHead( @@ -94,8 +182,30 @@ public static IEnumerable headerSelector, resultSelector); /// - /// TODO + /// Projects the head elements of the sequence exclusively with the + /// remainder elements of the sequence. Additional arguments specify + /// functions to delineate header elements, project the header + /// elements as well as the elements of the resulting sequence. /// + /// Type of source sequence elements. + /// Type of head projection. + /// + /// Type of elements of the returned sequence. + /// The source sequence. + /// Function that determines whether an + /// element of the source sequence is at the head. + /// Function that projects an + /// intermediate head representation given a list of head elements. + /// + /// Function that projects a result + /// given the head projection returned by + /// and one of the remainder elements. + /// + /// A sequence of elements returned by + /// . + /// + /// This operator uses deferred execution and streams its results. + /// public static IEnumerable SpillHead( @@ -115,8 +225,32 @@ public static IEnumerable } /// - /// TODO + /// Projects the head elements of the sequence exclusively with + /// the remainder elements of the sequence. Additional arguments + /// specify functions to delineate header elements (possibly based + /// on their index), project the header elements as well as the + /// elements of the resulting sequence. /// + /// Type of source sequence elements. + /// Type of head projection. + /// + /// Type of elements of the returned sequence. + /// The source sequence. + /// Function that determines whether an + /// element of the source sequence (along with its zero-based index) + /// is at the head. + /// Function that projects an + /// intermediate head representation given a list of head elements. + /// + /// Function that projects a result + /// given the head projection returned by + /// and one of the remainder elements along with its zero-based index. + /// + /// A sequence of elements returned by + /// . + /// + /// This operator uses deferred execution and streams its results. + /// public static IEnumerable SpillHead( @@ -135,43 +269,92 @@ public static IEnumerable } /// - /// TODO + /// Projects the head elements of the sequence exclusively with + /// the remainder elements of the sequence. Additional arguments + /// specify functions to choose and delineate header elements, + /// project the header elements as well as the elements of the + /// resulting sequence. /// + /// Type of source sequence elements. + /// Type of head match. + /// Type of head projection. + /// + /// Type of elements of the returned sequence. + /// The source sequence. + /// Function that determines whether an + /// element of the source sequence is a match for a head. + /// Function that projects an + /// intermediate head representation given a list of head matches. + /// + /// Function that projects a result + /// given the head projection returned by + /// and one of the remainder elements. + /// + /// A sequence of elements returned by + /// . + /// + /// This operator uses deferred execution and streams its results. + /// public static IEnumerable SpillHead( this IEnumerable source, - Func chooser, + Func matcher, Func, THead> headerSelector, Func resultSelector) { if (source == null) throw new ArgumentNullException(nameof(source)); - if (chooser == null) throw new ArgumentNullException(nameof(chooser)); + if (matcher == null) throw new ArgumentNullException(nameof(matcher)); if (headerSelector == null) throw new ArgumentNullException(nameof(headerSelector)); if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); - return source.SpillHead((e, _) => chooser(e), + return source.SpillHead((e, _) => matcher(e), headerSelector, (h, e, _) => resultSelector(h, e)); } /// - /// TODO + /// Projects the head elements of the sequence exclusively with + /// the remainder elements of the sequence. Additional arguments + /// specify functions to choose and delineate header elements + /// (possibly based on their index), project the header elements + /// as well as the elements of the resulting sequence. /// + /// Type of source sequence elements. + /// Type of head match. + /// Type of head projection. + /// + /// Type of elements of the returned sequence. + /// The source sequence. + /// Function that determines whether an + /// element of the source sequence (along with its zero-based index) + /// is a match for a head. + /// Function that projects an + /// intermediate head representation given a list of head matches. + /// + /// Function that projects a result + /// given the head projection returned by + /// and one of the remainder elements along with its zero-based index. + /// + /// A sequence of elements returned by + /// . + /// + /// This operator uses deferred execution and streams its results. + /// public static IEnumerable SpillHead( this IEnumerable source, - Func chooser, + Func matcher, Func, THead> headerSelector, Func resultSelector) { if (source == null) throw new ArgumentNullException(nameof(source)); - if (chooser == null) throw new ArgumentNullException(nameof(chooser)); + if (matcher == null) throw new ArgumentNullException(nameof(matcher)); if (headerSelector == null) throw new ArgumentNullException(nameof(headerSelector)); if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); - return source.SpillHead(chooser, + return source.SpillHead(matcher, null, h => new List { h }, (a, h) => { a.Add(h); return a; }, @@ -180,8 +363,37 @@ public static IEnumerable } /// - /// TODO + /// Projects the head elements of the sequence exclusively with + /// the remainder elements of the sequence. Additional arguments + /// specify functions to delineate header elements, fold and + /// project the header elements as well as project elements of + /// the resulting sequence. /// + /// Type of source sequence elements. + /// Type of head accumulator state. + /// Type of head projection. + /// + /// Type of elements of the returned sequence. + /// The source sequence. + /// Function that determines whether an + /// element of the source sequence is at the head. + /// The empty state when head is absent. + /// Function that seeds the accumulation state + /// with the initial head element. + /// Function that folds subsequent head + /// elements into the state. + /// Function that projects a single + /// head representation given the accumulated state of head elements. + /// + /// Function that projects a result + /// given the head projection returned by + /// and one of the remainder elements. + /// + /// A sequence of elements returned by + /// . + /// + /// This operator uses deferred execution and streams its results. + /// public static IEnumerable SpillHead( @@ -206,8 +418,38 @@ public static IEnumerable } /// - /// TODO + /// Projects the head elements of the sequence exclusively with + /// the remainder elements of the sequence. Additional arguments + /// specify functions to delineate header elements (possibly based on + /// their index), fold and project the header elements as well as + /// project elements of the resulting sequence. /// + /// Type of source sequence elements. + /// Type of head accumulator state. + /// Type of head projection. + /// + /// Type of elements of the returned sequence. + /// The source sequence. + /// Function that determines whether an + /// element of the source sequence (along with its zero-based index) + /// is at the head. + /// The empty state when head is absent. + /// Function that seeds the accumulation state + /// with the initial head element. + /// Function that folds subsequent head + /// elements into the state. + /// Function that projects a single + /// head representation given the accumulated state of head elements. + /// + /// Function that projects a result + /// given the head projection returned by + /// and one of the remainder elements along with its zero-based index. + /// + /// A sequence of elements returned by + /// . + /// + /// This operator uses deferred execution and streams its results. + /// public static IEnumerable SpillHead( @@ -231,13 +473,43 @@ public static IEnumerable } /// - /// TODO + /// Projects the head elements of the sequence exclusively with + /// the remainder elements of the sequence. Additional arguments + /// specify functions to choose and delineate header elements, + /// fold and project the header elements as well as project + /// elements of the resulting sequence. /// + /// Type of source sequence elements. + /// Type of head match. + /// Type of head accumulator state. + /// Type of head projection. + /// + /// Type of elements of the returned sequence. + /// The source sequence. + /// Function that determines whether an + /// element of the source sequence is a match for a head. + /// The empty state when head is absent. + /// Function that seeds the accumulation state + /// with the initial head element. + /// Function that folds subsequent head + /// elements into the state. + /// Function that projects a single + /// head representation given the accumulated state of head elements. + /// + /// Function that projects a result + /// given the head projection returned by + /// and one of the remainder elements. + /// + /// A sequence of elements returned by + /// . + /// + /// This operator uses deferred execution and streams its results. + /// public static IEnumerable SpillHead( this IEnumerable source, - Func chooser, + Func matcher, TState empty, Func seeder, Func accumulator, @@ -245,25 +517,56 @@ public static IEnumerable Func resultSelector) { if (source == null) throw new ArgumentNullException(nameof(source)); - if (chooser == null) throw new ArgumentNullException(nameof(chooser)); + if (matcher == null) throw new ArgumentNullException(nameof(matcher)); if (seeder == null) throw new ArgumentNullException(nameof(seeder)); if (accumulator == null) throw new ArgumentNullException(nameof(accumulator)); if (headerSelector == null) throw new ArgumentNullException(nameof(headerSelector)); if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); - return source.SpillHead((e, _) => chooser(e), + return source.SpillHead((e, _) => matcher(e), empty, seeder, accumulator, headerSelector, (h, e, _) => resultSelector(h, e)); } /// - /// TODO + /// Projects the head elements of the sequence exclusively with + /// the remainder elements of the sequence. Additional arguments + /// specify functions to choose and delineate header elements + /// (possibly based on their index), fold and project the header + /// elements as well as project elements of the resulting sequence. /// + /// Type of source sequence elements. + /// Type of head match. + /// Type of head accumulator state. + /// Type of head projection. + /// + /// Type of elements of the returned sequence. + /// The source sequence. + /// Function that determines whether an + /// element of the source sequence (along with its zero-based index) + /// is a match for a head. + /// The empty state when head is absent. + /// Function that seeds the accumulation state + /// with the initial head element. + /// Function that folds subsequent head + /// elements into the state. + /// Function that projects a single + /// head representation given the accumulated state of head elements. + /// + /// Function that projects a result + /// given the head projection returned by + /// and one of the remainder elements along with its zero-based index. + /// + /// A sequence of elements returned by + /// . + /// + /// This operator uses deferred execution and streams its results. + /// public static IEnumerable SpillHead( this IEnumerable source, - Func chooser, + Func matcher, TState empty, Func seeder, Func accumulator, @@ -271,7 +574,7 @@ public static IEnumerable Func resultSelector) { if (source == null) throw new ArgumentNullException(nameof(source)); - if (chooser == null) throw new ArgumentNullException(nameof(chooser)); + if (matcher == null) throw new ArgumentNullException(nameof(matcher)); if (seeder == null) throw new ArgumentNullException(nameof(seeder)); if (accumulator == null) throw new ArgumentNullException(nameof(accumulator)); if (headerSelector == null) throw new ArgumentNullException(nameof(headerSelector)); @@ -283,7 +586,7 @@ public static IEnumerable if (!e.MoveNext()) yield break; var i = 0; - var (span, fm) = chooser(e.Current, i); + var (span, fm) = matcher(e.Current, i); var state = span ? seeder(fm) : empty; if (span) { @@ -291,7 +594,7 @@ public static IEnumerable { if (!e.MoveNext()) yield break; - if (chooser(e.Current, ++i) is (true, var m)) + if (matcher(e.Current, ++i) is (true, var m)) state = accumulator(state, m); else break; diff --git a/README.md b/README.md index 9dab6fb94..02ec371c2 100644 --- a/README.md +++ b/README.md @@ -573,10 +573,16 @@ This method has 2 overloads. ### SpillHead -TODO +Projects the head elements of a sequence exclusively with the remainder +elements of the sequence. Arguments of various overloads specify functions to +choose and delineate header elements (possibly based on their index), fold and +project the header elements as well as project elements of the resulting +sequence. This method has 13 overloads. +Some of the simpler overloads enable pairing the one or more head elements +with the remainder elements of a sequence. ### Split From bd3d53ff3ec934d414c2ab215db9741b074be3bf Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Tue, 11 Aug 2020 13:10:18 +0200 Subject: [PATCH 28/32] Refresh doc comments for generated counterpart --- MoreLinq/Extensions.g.cs | 345 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 324 insertions(+), 21 deletions(-) diff --git a/MoreLinq/Extensions.g.cs b/MoreLinq/Extensions.g.cs index e9af12074..5b95dc8b6 100644 --- a/MoreLinq/Extensions.g.cs +++ b/MoreLinq/Extensions.g.cs @@ -5286,15 +5286,38 @@ public static IEnumerable SortedMerge(this IEnumerable - /// TODO + /// Couples the first/head element of the sequence exclusively with + /// the remainder elements of the sequence. /// + /// Type of source sequence elements. + /// The source sequence. + /// + /// A sequence with the head element coupled together with the + /// remainder elements of the source sequence. + /// + /// This operator uses deferred execution and streams its results. + /// public static IEnumerable<(T Head, T Item)> SpillHead(this IEnumerable source) => MoreEnumerable. SpillHead(source); /// - /// TODO + /// Projects the first/head element of the sequence exclusively with + /// the remainder elements of the sequence. An additional argument + /// specifies a function that receives an element of sequence + /// together with the first/head element and returns a projection. /// + /// Type of source sequence elements. + /// + /// Type of elements of the returned sequence. + /// The source sequence. + /// Function that projects a result + /// given the head and one of the remainder elements. + /// A sequence of elements returned by + /// . + /// + /// This operator uses deferred execution and streams its results. + /// public static IEnumerable SpillHead( @@ -5302,8 +5325,28 @@ public static IEnumerable Func resultSelector) => MoreEnumerable. SpillHead(source, resultSelector); /// - /// TODO + /// Projects the first/head element of the sequence exclusively with + /// the remainder elements of the sequence. Additional arguments + /// specify functions to project the head as well as the elements + /// of the resulting sequence. /// + /// Type of source sequence elements. + /// Type of head projection. + /// + /// Type of elements of the returned sequence. + /// The source sequence. + /// Function that projects an + /// intermediate head representation. + /// + /// Function that projects a result + /// given the head projection returned by + /// and one of the remainder elements. + /// + /// A sequence of elements returned by + /// . + /// + /// This operator uses deferred execution and streams its results. + /// public static IEnumerable SpillHead( @@ -5313,8 +5356,30 @@ public static IEnumerable => MoreEnumerable. SpillHead(source, headerSelector, resultSelector); /// - /// TODO + /// Projects the head elements of the sequence exclusively with the + /// remainder elements of the sequence. Additional arguments specify + /// functions to project the head as well as the elements of the + /// resulting sequence. /// + /// Type of source sequence elements. + /// Type of head projection. + /// + /// Type of elements of the returned sequence. + /// The source sequence. + /// Count head elements to collect. + /// Function that projects an + /// intermediate head representation given a list of exactly + /// head elements. + /// + /// Function that projects a result + /// given the head projection returned by + /// and one of the remainder elements. + /// + /// A sequence of elements returned by + /// . + /// + /// This operator uses deferred execution and streams its results. + /// public static IEnumerable SpillHead( @@ -5325,8 +5390,31 @@ public static IEnumerable => MoreEnumerable. SpillHead(source, count, headerSelector, resultSelector); /// - /// TODO + /// Projects the head elements of the sequence exclusively with the + /// remainder elements of the sequence. Additional arguments specify + /// functions to project the head as well as the elements (along with + /// their index) of the resulting sequence. /// + /// Type of source sequence elements. + /// Type of head projection. + /// + /// Type of elements of the returned sequence. + /// The source sequence. + /// Count head elements to collect. + /// Function that projects an + /// intermediate head representation given a list of exactly + /// head elements. + /// + /// Function that projects a result given + /// the head projection returned by + /// and one of the remainder elements along with its zero-based index + /// (where zero is the first non-head element). + /// + /// A sequence of elements returned by + /// . + /// + /// This operator uses deferred execution and streams its results. + /// public static IEnumerable SpillHead( @@ -5336,8 +5424,30 @@ public static IEnumerable Func resultSelector) => MoreEnumerable. SpillHead(source, count, headerSelector, resultSelector); /// - /// TODO + /// Projects the head elements of the sequence exclusively with the + /// remainder elements of the sequence. Additional arguments specify + /// functions to delineate header elements, project the header + /// elements as well as the elements of the resulting sequence. /// + /// Type of source sequence elements. + /// Type of head projection. + /// + /// Type of elements of the returned sequence. + /// The source sequence. + /// Function that determines whether an + /// element of the source sequence is at the head. + /// Function that projects an + /// intermediate head representation given a list of head elements. + /// + /// Function that projects a result + /// given the head projection returned by + /// and one of the remainder elements. + /// + /// A sequence of elements returned by + /// . + /// + /// This operator uses deferred execution and streams its results. + /// public static IEnumerable SpillHead( @@ -5348,8 +5458,32 @@ public static IEnumerable => MoreEnumerable. SpillHead(source, predicate, headerSelector, resultSelector); /// - /// TODO + /// Projects the head elements of the sequence exclusively with + /// the remainder elements of the sequence. Additional arguments + /// specify functions to delineate header elements (possibly based + /// on their index), project the header elements as well as the + /// elements of the resulting sequence. /// + /// Type of source sequence elements. + /// Type of head projection. + /// + /// Type of elements of the returned sequence. + /// The source sequence. + /// Function that determines whether an + /// element of the source sequence (along with its zero-based index) + /// is at the head. + /// Function that projects an + /// intermediate head representation given a list of head elements. + /// + /// Function that projects a result + /// given the head projection returned by + /// and one of the remainder elements along with its zero-based index. + /// + /// A sequence of elements returned by + /// . + /// + /// This operator uses deferred execution and streams its results. + /// public static IEnumerable SpillHead( @@ -5360,32 +5494,110 @@ public static IEnumerable => MoreEnumerable. SpillHead(source, predicate, headerSelector, resultSelector); /// - /// TODO + /// Projects the head elements of the sequence exclusively with + /// the remainder elements of the sequence. Additional arguments + /// specify functions to choose and delineate header elements, + /// project the header elements as well as the elements of the + /// resulting sequence. /// + /// Type of source sequence elements. + /// Type of head match. + /// Type of head projection. + /// + /// Type of elements of the returned sequence. + /// The source sequence. + /// Function that determines whether an + /// element of the source sequence is a match for a head. + /// Function that projects an + /// intermediate head representation given a list of head matches. + /// + /// Function that projects a result + /// given the head projection returned by + /// and one of the remainder elements. + /// + /// A sequence of elements returned by + /// . + /// + /// This operator uses deferred execution and streams its results. + /// public static IEnumerable SpillHead( this IEnumerable source, - Func chooser, + Func matcher, Func, THead> headerSelector, Func resultSelector) - => MoreEnumerable. SpillHead(source, chooser, headerSelector, resultSelector); + => MoreEnumerable. SpillHead(source, matcher, headerSelector, resultSelector); /// - /// TODO + /// Projects the head elements of the sequence exclusively with + /// the remainder elements of the sequence. Additional arguments + /// specify functions to choose and delineate header elements + /// (possibly based on their index), project the header elements + /// as well as the elements of the resulting sequence. /// + /// Type of source sequence elements. + /// Type of head match. + /// Type of head projection. + /// + /// Type of elements of the returned sequence. + /// The source sequence. + /// Function that determines whether an + /// element of the source sequence (along with its zero-based index) + /// is a match for a head. + /// Function that projects an + /// intermediate head representation given a list of head matches. + /// + /// Function that projects a result + /// given the head projection returned by + /// and one of the remainder elements along with its zero-based index. + /// + /// A sequence of elements returned by + /// . + /// + /// This operator uses deferred execution and streams its results. + /// public static IEnumerable SpillHead( this IEnumerable source, - Func chooser, + Func matcher, Func, THead> headerSelector, Func resultSelector) - => MoreEnumerable. SpillHead(source, chooser, headerSelector, resultSelector); + => MoreEnumerable. SpillHead(source, matcher, headerSelector, resultSelector); /// - /// TODO + /// Projects the head elements of the sequence exclusively with + /// the remainder elements of the sequence. Additional arguments + /// specify functions to delineate header elements, fold and + /// project the header elements as well as project elements of + /// the resulting sequence. /// + /// Type of source sequence elements. + /// Type of head accumulator state. + /// Type of head projection. + /// + /// Type of elements of the returned sequence. + /// The source sequence. + /// Function that determines whether an + /// element of the source sequence is at the head. + /// The empty state when head is absent. + /// Function that seeds the accumulation state + /// with the initial head element. + /// Function that folds subsequent head + /// elements into the state. + /// Function that projects a single + /// head representation given the accumulated state of head elements. + /// + /// Function that projects a result + /// given the head projection returned by + /// and one of the remainder elements. + /// + /// A sequence of elements returned by + /// . + /// + /// This operator uses deferred execution and streams its results. + /// public static IEnumerable SpillHead( @@ -5399,8 +5611,38 @@ public static IEnumerable => MoreEnumerable. SpillHead(source, predicate, empty, seeder, accumulator, headerSelector, resultSelector); /// - /// TODO + /// Projects the head elements of the sequence exclusively with + /// the remainder elements of the sequence. Additional arguments + /// specify functions to delineate header elements (possibly based on + /// their index), fold and project the header elements as well as + /// project elements of the resulting sequence. /// + /// Type of source sequence elements. + /// Type of head accumulator state. + /// Type of head projection. + /// + /// Type of elements of the returned sequence. + /// The source sequence. + /// Function that determines whether an + /// element of the source sequence (along with its zero-based index) + /// is at the head. + /// The empty state when head is absent. + /// Function that seeds the accumulation state + /// with the initial head element. + /// Function that folds subsequent head + /// elements into the state. + /// Function that projects a single + /// head representation given the accumulated state of head elements. + /// + /// Function that projects a result + /// given the head projection returned by + /// and one of the remainder elements along with its zero-based index. + /// + /// A sequence of elements returned by + /// . + /// + /// This operator uses deferred execution and streams its results. + /// public static IEnumerable SpillHead( @@ -5414,34 +5656,95 @@ public static IEnumerable => MoreEnumerable. SpillHead(source, predicate, empty, seeder, accumulator, headerSelector, resultSelector); /// - /// TODO + /// Projects the head elements of the sequence exclusively with + /// the remainder elements of the sequence. Additional arguments + /// specify functions to choose and delineate header elements, + /// fold and project the header elements as well as project + /// elements of the resulting sequence. /// + /// Type of source sequence elements. + /// Type of head match. + /// Type of head accumulator state. + /// Type of head projection. + /// + /// Type of elements of the returned sequence. + /// The source sequence. + /// Function that determines whether an + /// element of the source sequence is a match for a head. + /// The empty state when head is absent. + /// Function that seeds the accumulation state + /// with the initial head element. + /// Function that folds subsequent head + /// elements into the state. + /// Function that projects a single + /// head representation given the accumulated state of head elements. + /// + /// Function that projects a result + /// given the head projection returned by + /// and one of the remainder elements. + /// + /// A sequence of elements returned by + /// . + /// + /// This operator uses deferred execution and streams its results. + /// public static IEnumerable SpillHead( this IEnumerable source, - Func chooser, + Func matcher, TState empty, Func seeder, Func accumulator, Func headerSelector, Func resultSelector) - => MoreEnumerable. SpillHead(source, chooser, empty, seeder, accumulator, headerSelector, resultSelector); + => MoreEnumerable. SpillHead(source, matcher, empty, seeder, accumulator, headerSelector, resultSelector); /// - /// TODO + /// Projects the head elements of the sequence exclusively with + /// the remainder elements of the sequence. Additional arguments + /// specify functions to choose and delineate header elements + /// (possibly based on their index), fold and project the header + /// elements as well as project elements of the resulting sequence. /// + /// Type of source sequence elements. + /// Type of head match. + /// Type of head accumulator state. + /// Type of head projection. + /// + /// Type of elements of the returned sequence. + /// The source sequence. + /// Function that determines whether an + /// element of the source sequence (along with its zero-based index) + /// is a match for a head. + /// The empty state when head is absent. + /// Function that seeds the accumulation state + /// with the initial head element. + /// Function that folds subsequent head + /// elements into the state. + /// Function that projects a single + /// head representation given the accumulated state of head elements. + /// + /// Function that projects a result + /// given the head projection returned by + /// and one of the remainder elements along with its zero-based index. + /// + /// A sequence of elements returned by + /// . + /// + /// This operator uses deferred execution and streams its results. + /// public static IEnumerable SpillHead( this IEnumerable source, - Func chooser, + Func matcher, TState empty, Func seeder, Func accumulator, Func headerSelector, Func resultSelector) - => MoreEnumerable. SpillHead(source, chooser, empty, seeder, accumulator, headerSelector, resultSelector); + => MoreEnumerable. SpillHead(source, matcher, empty, seeder, accumulator, headerSelector, resultSelector); } From aa56aa28018360a2467476f1037b14ce544292ad Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Tue, 11 Aug 2020 14:30:47 +0200 Subject: [PATCH 29/32] Remove args redundant with overload --- MoreLinq.Test/SpillHeadTest.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/MoreLinq.Test/SpillHeadTest.cs b/MoreLinq.Test/SpillHeadTest.cs index d56e73122..c123f41e6 100644 --- a/MoreLinq.Test/SpillHeadTest.cs +++ b/MoreLinq.Test/SpillHeadTest.cs @@ -138,9 +138,6 @@ from e in Regex.Split(csv.Trim(), @"\r?\n") .Select(line => line.Trim()) .SpillHead(h => Regex.Match(h, @"^;\s*(\w+)") is var m & m.Success ? (true, m.Groups[1].Value) : default, - Enumerable.Empty(), - MoreEnumerable.Return, - (a, h) => a.Append(h), h => MoreEnumerable.Return(h.Index() .ToDictionary(e => e.Value, e => e.Key)) .SelectMany(d => new[] { "a", "b", "c" }, From e000c63cd8c46bf630251476f70f18b3edeee75e Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Tue, 11 Aug 2020 14:51:17 +0200 Subject: [PATCH 30/32] Add more tests --- MoreLinq.Test/SpillHeadTest.cs | 39 ++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/MoreLinq.Test/SpillHeadTest.cs b/MoreLinq.Test/SpillHeadTest.cs index c123f41e6..779aed7ec 100644 --- a/MoreLinq.Test/SpillHeadTest.cs +++ b/MoreLinq.Test/SpillHeadTest.cs @@ -71,6 +71,45 @@ public void InsufficientElementsPerHeadCount(int count) Assert.That(result, Is.Empty); } + [Test] + public void PredicatedHeads() + { + var heads = new[] { "head1", "head2", "head3" }; + var data = new[] { "foo", "bar", "baz" }; + var result = heads.Concat(data) + .SpillHead(h => Regex.IsMatch(h, "^head[0-9]$"), + hs => string.Join("|", hs), + (h, e) => new { Head = h, Data = e }); + + Assert.That(result, Is.EqualTo(new[] + { + new { Head = "head1|head2|head3", Data = "foo" }, + new { Head = "head1|head2|head3", Data = "bar" }, + new { Head = "head1|head2|head3", Data = "baz" }, + })); + } + + [Test] + public void CustomAccumulation() + { + var heads = new[] { "head1", "head2", "head3" }; + var data = new[] { "foo", "bar", "baz" }; + var result = heads.Concat(data) + .SpillHead(h => Regex.IsMatch(h, "^head[0-9]$"), + Enumerable.Empty(), + MoreEnumerable.Return, + (hs, h) => hs.Append(h), + hs => string.Join("|", hs), + (h, e) => new { Head = h, Data = e }); + + Assert.That(result, Is.EqualTo(new[] + { + new { Head = "head1|head2|head3", Data = "foo" }, + new { Head = "head1|head2|head3", Data = "bar" }, + new { Head = "head1|head2|head3", Data = "baz" }, + })); + } + [Test] public void NoneSatisfyHeadPredicate() { From 71f9120a0f3f32ecc72d552b2d973fccb23d4342 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Tue, 11 Aug 2020 15:04:44 +0200 Subject: [PATCH 31/32] Test single-pass iteration & enumerator disposal --- MoreLinq.Test/SpillHeadTest.cs | 95 +++++++++++++++++----------------- 1 file changed, 48 insertions(+), 47 deletions(-) diff --git a/MoreLinq.Test/SpillHeadTest.cs b/MoreLinq.Test/SpillHeadTest.cs index 779aed7ec..841bd90af 100644 --- a/MoreLinq.Test/SpillHeadTest.cs +++ b/MoreLinq.Test/SpillHeadTest.cs @@ -29,7 +29,8 @@ public class SpillHeadTest [Test] public void RepeatHeadElementWithRest() { - var result = Enumerable.Range(5, 6).SpillHead(); + using var ts = Enumerable.Range(5, 6).AsTestingSequence(); + var result = ts.SpillHead(); Assert.That(result, Is.EqualTo(new[] { @@ -40,16 +41,16 @@ public void RepeatHeadElementWithRest() [Test] public void HeadElementOnly() { - var result = new[] { "head" }.SpillHead(); + using var ts = new[] { "head" }.AsTestingSequence(); + var result = ts.SpillHead(); Assert.That(result, Is.Empty); } [Test] public void RepeatHeadElementsWithRest() { - var result = - Enumerable.Range(5, 6) - .SpillHead(2, hs => hs, (h, d) => (h[0], h[1], d)); + using var ts = Enumerable.Range(5, 6).AsTestingSequence(); + var result = ts.SpillHead(2, hs => hs, (h, d) => (h[0], h[1], d)); Assert.That(result, Is.EqualTo(new[] { @@ -63,10 +64,10 @@ public void RepeatHeadElementsWithRest() [TestCase(3)] public void InsufficientElementsPerHeadCount(int count) { - var result = Enumerable.Repeat("head", count) - .SpillHead(3, - BreakingFunc.Of, object>(), - BreakingFunc.Of()); + using var ts = Enumerable.Repeat("head", count).AsTestingSequence(); + var result = ts.SpillHead(3, + BreakingFunc.Of, object>(), + BreakingFunc.Of()); Assert.That(result, Is.Empty); } @@ -76,10 +77,10 @@ public void PredicatedHeads() { var heads = new[] { "head1", "head2", "head3" }; var data = new[] { "foo", "bar", "baz" }; - var result = heads.Concat(data) - .SpillHead(h => Regex.IsMatch(h, "^head[0-9]$"), - hs => string.Join("|", hs), - (h, e) => new { Head = h, Data = e }); + using var ts = heads.Concat(data).AsTestingSequence(); + var result = ts.SpillHead(h => Regex.IsMatch(h, "^head[0-9]$"), + hs => string.Join("|", hs), + (h, e) => new { Head = h, Data = e }); Assert.That(result, Is.EqualTo(new[] { @@ -94,13 +95,13 @@ public void CustomAccumulation() { var heads = new[] { "head1", "head2", "head3" }; var data = new[] { "foo", "bar", "baz" }; - var result = heads.Concat(data) - .SpillHead(h => Regex.IsMatch(h, "^head[0-9]$"), - Enumerable.Empty(), - MoreEnumerable.Return, - (hs, h) => hs.Append(h), - hs => string.Join("|", hs), - (h, e) => new { Head = h, Data = e }); + using var ts = heads.Concat(data).AsTestingSequence(); + var result = ts.SpillHead(h => Regex.IsMatch(h, "^head[0-9]$"), + Enumerable.Empty(), + MoreEnumerable.Return, + (hs, h) => hs.Append(h), + hs => string.Join("|", hs), + (h, e) => new { Head = h, Data = e }); Assert.That(result, Is.EqualTo(new[] { @@ -113,7 +114,7 @@ public void CustomAccumulation() [Test] public void NoneSatisfyHeadPredicate() { - var words = new[] { "foo", "bar", "baz" }; + using var words = new[] { "foo", "bar", "baz" }.AsTestingSequence(); var result = words.SpillHead(e => e == "head", hs => hs.Count, (hc, e) => new { HeadCount = 0, Data = e }); @@ -135,23 +136,21 @@ public void Csv() 4,6,5 7,9,8"; + var rows = + from line in Regex.Split(csv.Trim(), @"\r?\n") + select line.Split(',').Select(f => f.Trim()).ToArray(); + + using var ts = rows.AsTestingSequence(); var result = - from rows in new[] - { - from line in Regex.Split(csv.Trim(), @"\r?\n") - select line.Split(',').Select(f => f.Trim()).ToArray() - } - from row in - rows.SpillHead( - h => MoreEnumerable.Return(h.Index() - .ToDictionary(e => e.Value, e => e.Key)) - .SelectMany(d => new[] { "a", "b", "c" }, - (d, n) => d[n]) - .Select(i => Func((string[] s) => s[i])) - .ToArray(), - (bs, r) => bs.Select(b => int.Parse(b(r), CultureInfo.InvariantCulture)) - .Fold((a, b, c) => new { A = a, B = b, C = c })) - select row; + ts.SpillHead( + h => MoreEnumerable.Return(h.Index() + .ToDictionary(e => e.Value, e => e.Key)) + .SelectMany(d => new[] { "a", "b", "c" }, + (d, n) => d[n]) + .Select(i => Func((string[] s) => s[i])) + .ToArray(), + (bs, r) => bs.Select(b => int.Parse(b(r), CultureInfo.InvariantCulture)) + .Fold((a, b, c) => new { A = a, B = b, C = c })); Assert.That(result, Is.EqualTo(new[] { @@ -172,18 +171,20 @@ public void CsvWithColumnsInCommentLines() 4,5,6 7,8,9"; + using var ts = Regex.Split(csv.Trim(), @"\r?\n") + .Select(line => line.Trim()) + .AsTestingSequence(); + var result = from e in - Regex.Split(csv.Trim(), @"\r?\n") - .Select(line => line.Trim()) - .SpillHead(h => Regex.Match(h, @"^;\s*(\w+)") is var m & m.Success ? (true, m.Groups[1].Value) : default, - h => MoreEnumerable.Return(h.Index() - .ToDictionary(e => e.Value, e => e.Key)) - .SelectMany(d => new[] { "a", "b", "c" }, - (d, n) => d[n]) - .Select(i => Func((string[] s) => s[i])) - .ToArray(), - (bs, r) => new { Bindings = bs, Fields = r.Split(',') }) + ts.SpillHead(h => Regex.Match(h, @"^;\s*(\w+)") is var m & m.Success ? (true, m.Groups[1].Value) : default, + h => MoreEnumerable.Return(h.Index() + .ToDictionary(e => e.Value, e => e.Key)) + .SelectMany(d => new[] { "a", "b", "c" }, + (d, n) => d[n]) + .Select(i => Func((string[] s) => s[i])) + .ToArray(), + (bs, r) => new { Bindings = bs, Fields = r.Split(',') }) select e.Bindings .Select(b => int.Parse(b(e.Fields), CultureInfo.InvariantCulture)) .Fold((a, b, c) => new { A = a, B = b, C = c }); From 4219e175169fe0cbe058f1b1aac30b2660a7f8aa Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Tue, 11 Aug 2020 16:06:06 +0200 Subject: [PATCH 32/32] Make testing sequence setup more readable --- MoreLinq.Test/SpillHeadTest.cs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/MoreLinq.Test/SpillHeadTest.cs b/MoreLinq.Test/SpillHeadTest.cs index 841bd90af..f20618160 100644 --- a/MoreLinq.Test/SpillHeadTest.cs +++ b/MoreLinq.Test/SpillHeadTest.cs @@ -29,7 +29,7 @@ public class SpillHeadTest [Test] public void RepeatHeadElementWithRest() { - using var ts = Enumerable.Range(5, 6).AsTestingSequence(); + using var ts = TestingSequence.Of(5, 6, 7, 8, 9, 10); var result = ts.SpillHead(); Assert.That(result, Is.EqualTo(new[] @@ -41,7 +41,7 @@ public void RepeatHeadElementWithRest() [Test] public void HeadElementOnly() { - using var ts = new[] { "head" }.AsTestingSequence(); + using var ts = TestingSequence.Of("head"); var result = ts.SpillHead(); Assert.That(result, Is.Empty); } @@ -49,7 +49,7 @@ public void HeadElementOnly() [Test] public void RepeatHeadElementsWithRest() { - using var ts = Enumerable.Range(5, 6).AsTestingSequence(); + using var ts = TestingSequence.Of(5, 6, 7, 8, 9, 10); var result = ts.SpillHead(2, hs => hs, (h, d) => (h[0], h[1], d)); Assert.That(result, Is.EqualTo(new[] @@ -75,9 +75,9 @@ public void InsufficientElementsPerHeadCount(int count) [Test] public void PredicatedHeads() { - var heads = new[] { "head1", "head2", "head3" }; - var data = new[] { "foo", "bar", "baz" }; - using var ts = heads.Concat(data).AsTestingSequence(); + using var ts = TestingSequence.Of("head1", "head2", "head3", + "foo", "bar", "baz"); + var result = ts.SpillHead(h => Regex.IsMatch(h, "^head[0-9]$"), hs => string.Join("|", hs), (h, e) => new { Head = h, Data = e }); @@ -93,9 +93,9 @@ public void PredicatedHeads() [Test] public void CustomAccumulation() { - var heads = new[] { "head1", "head2", "head3" }; - var data = new[] { "foo", "bar", "baz" }; - using var ts = heads.Concat(data).AsTestingSequence(); + using var ts = TestingSequence.Of("head1", "head2", "head3", + "foo", "bar", "baz"); + var result = ts.SpillHead(h => Regex.IsMatch(h, "^head[0-9]$"), Enumerable.Empty(), MoreEnumerable.Return, @@ -114,7 +114,7 @@ public void CustomAccumulation() [Test] public void NoneSatisfyHeadPredicate() { - using var words = new[] { "foo", "bar", "baz" }.AsTestingSequence(); + using var words = TestingSequence.Of("foo", "bar", "baz"); var result = words.SpillHead(e => e == "head", hs => hs.Count, (hc, e) => new { HeadCount = 0, Data = e });