Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add SpillHead #753

Open
wants to merge 35 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
7839032
Initial version SpillSpan (WIP)
atifaziz Aug 5, 2020
359bc56
Fix copyright year
atifaziz Aug 6, 2020
1c02293
Remove Kons dependency
atifaziz Aug 6, 2020
d9fee41
Merge branch 'master' into spill-span
atifaziz Aug 6, 2020
3706b77
Use terser Func expression
atifaziz Aug 6, 2020
c639553
Move out of experimental namespace
atifaziz Aug 7, 2020
c1fe89d
Implement all overloads in terms of the core
atifaziz Aug 7, 2020
a3b2a29
Have core overload provide index
atifaziz Aug 7, 2020
14deb60
Fix test name
atifaziz Aug 7, 2020
1fdbf8e
Use the simpler overload in the basic test
atifaziz Aug 7, 2020
0d674dd
Throw "InvalidOperationException" if there's no header
atifaziz Aug 7, 2020
26f1d96
Avoid list allocation for head count == 1
atifaziz Aug 7, 2020
662b3fa
Remove the throw case that is impossible
atifaziz Aug 7, 2020
57d4c71
Complete previous commit
atifaziz Aug 7, 2020
bb32281
Rename to SpillHead
atifaziz Aug 7, 2020
4c38d38
Save final list copy
atifaziz Aug 10, 2020
2582f4a
Add complementary overloads
atifaziz Aug 10, 2020
3b7397f
Expand generic type parameter names
atifaziz Aug 10, 2020
9820013
Review code layout
atifaziz Aug 10, 2020
f56c76f
Discard unused args
atifaziz Aug 10, 2020
368e5cc
Add test for simplest case
atifaziz Aug 10, 2020
fbfe12a
Fix initial head element missing in list
atifaziz Aug 10, 2020
1ec6e44
List in package description
atifaziz Aug 10, 2020
c61f8e0
Add to-do in README doc
atifaziz Aug 10, 2020
9906ca1
Add test case for when there is just head
atifaziz Aug 11, 2020
ebc6b1f
Fix head loop bug
atifaziz Aug 11, 2020
af3ad95
Test for when none satisfy head predicate
atifaziz Aug 11, 2020
6c80586
Complete documentation
atifaziz Aug 11, 2020
bd3d53f
Refresh doc comments for generated counterpart
atifaziz Aug 11, 2020
aa56aa2
Remove args redundant with overload
atifaziz Aug 11, 2020
e000c63
Add more tests
atifaziz Aug 11, 2020
71f9120
Test single-pass iteration & enumerator disposal
atifaziz Aug 11, 2020
4219e17
Make testing sequence setup more readable
atifaziz Aug 11, 2020
aac8fea
Merge branch 'master' into spill-span
atifaziz Aug 11, 2020
25c256a
Merge remote-tracking branch 'upstream/master' into spill-span
atifaziz Sep 22, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions MoreLinq.Test/Enumerable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ public static bool Any<TSource>(this IEnumerable<TSource> source) =>
public static bool Any<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) =>
LinqEnumerable.Any(source, predicate);

public static IEnumerable<TSource> Append<TSource>(this IEnumerable<TSource> source, TSource element) =>
LinqEnumerable.Append(source, element);

public static IEnumerable<TSource> AsEnumerable<TSource>(this IEnumerable<TSource> source) =>
LinqEnumerable.AsEnumerable(source);

Expand Down
200 changes: 200 additions & 0 deletions MoreLinq.Test/SpillHeadTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
#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.Test
{
using System.Collections.Generic;
using System.Globalization;
using System.Text.RegularExpressions;
using NUnit.Framework;
using static FuncModule;

[TestFixture]
public class SpillHeadTest
{
[Test]
public void RepeatHeadElementWithRest()
{
using var ts = TestingSequence.Of(5, 6, 7, 8, 9, 10);
var result = ts.SpillHead();

Assert.That(result, Is.EqualTo(new[]
{
(5, 6), (5, 7), (5, 8), (5, 9), (5, 10)
}));
}

[Test]
public void HeadElementOnly()
{
using var ts = TestingSequence.Of("head");
var result = ts.SpillHead();
Assert.That(result, Is.Empty);
}

[Test]
public void RepeatHeadElementsWithRest()
{
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[]
{
(5, 6, 7), (5, 6, 8), (5, 6, 9), (5, 6, 10)
}));
}

[TestCase(0)]
[TestCase(1)]
[TestCase(2)]
[TestCase(3)]
public void InsufficientElementsPerHeadCount(int count)
{
using var ts = Enumerable.Repeat("head", count).AsTestingSequence();
var result = ts.SpillHead(3,
BreakingFunc.Of<List<string>, object>(),
BreakingFunc.Of<object, string, object>());

Assert.That(result, Is.Empty);
}

[Test]
public void PredicatedHeads()
{
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 });

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()
{
using var ts = TestingSequence.Of("head1", "head2", "head3",
"foo", "bar", "baz");

var result = ts.SpillHead(h => Regex.IsMatch(h, "^head[0-9]$"),
() => [],
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()
{
using var words = TestingSequence.Of("foo", "bar", "baz");
var result = words.SpillHead(e => e == "head",
hs => hs.Count,
(hc, e) => new { HeadCount = hc, 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()
{
const string csv = @"
a,c,b
1,3,2
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 =
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[]
{
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";

using var ts = Regex.Split(csv.Trim(), @"\r?\n")
.Select(line => line.Trim())
.AsTestingSequence();

var result =
from e in
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 });

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 },
}));
}
}
}
Loading
Loading