From dc1497baee822eed440c151c90afab7870252595 Mon Sep 17 00:00:00 2001 From: Stuart Turner Date: Wed, 10 May 2023 11:45:15 -0500 Subject: [PATCH] `TagFirstLast`: `IList<>` implementation --- Source/SuperLinq/TagFirstLast.cs | 42 +++++++++++++++++ Tests/SuperLinq.Test/TagFirstLastTest.cs | 57 ++++++++++-------------- 2 files changed, 66 insertions(+), 33 deletions(-) diff --git a/Source/SuperLinq/TagFirstLast.cs b/Source/SuperLinq/TagFirstLast.cs index 936e47ba..0eac09e6 100644 --- a/Source/SuperLinq/TagFirstLast.cs +++ b/Source/SuperLinq/TagFirstLast.cs @@ -66,6 +66,9 @@ public static IEnumerable TagFirstLast(this IEnumerab Guard.IsNotNull(source); Guard.IsNotNull(resultSelector); + if (source is IList list) + return new TagFirstLastIterator(list, resultSelector); + return Core(source, resultSelector); static IEnumerable Core(IEnumerable source, Func resultSelector) @@ -88,4 +91,43 @@ static IEnumerable Core(IEnumerable source, Func : ListIterator + { + private readonly IList _source; + private readonly Func _resultSelector; + + public TagFirstLastIterator(IList source, Func resultSelector) + { + _source = source; + _resultSelector = resultSelector; + } + + public override int Count => _source.Count; + + protected override IEnumerable GetEnumerable() + { + if (_source.Count <= 1) + { + if (_source.Count == 1) + yield return _resultSelector(_source[0], true, true); + yield break; + } + + yield return _resultSelector(_source[0], true, false); + + var cnt = (uint)_source.Count - 1; + + for (var i = 1; i < cnt; i++) + yield return _resultSelector(_source[i], false, false); + + yield return _resultSelector(_source[^1], false, true); + } + + protected override TResult ElementAt(int index) + { + Guard.IsBetweenOrEqualTo(index, 0, _source.Count - 1); + return _resultSelector(_source[index], index == 0, index == _source.Count - 1); + } + } } diff --git a/Tests/SuperLinq.Test/TagFirstLastTest.cs b/Tests/SuperLinq.Test/TagFirstLastTest.cs index 55087a3a..65cd177d 100644 --- a/Tests/SuperLinq.Test/TagFirstLastTest.cs +++ b/Tests/SuperLinq.Test/TagFirstLastTest.cs @@ -9,44 +9,35 @@ public void TagFirstLastIsLazy() _ = new BreakingSequence().TagFirstLast(BreakingFunc.Of()); } - [Fact] - public void TagFirstLastEmptySequence() - { - using var sequence = TestingSequence.Of(); - - var result = sequence.TagFirstLast(); - result.AssertSequenceEqual(); - } - - [Fact] - public void TagFirstLastWithSourceSequenceOfOne() + public static IEnumerable GetSourceSequences() => + Enumerable.Range(0, 4) + .SelectMany(n => + Enumerable.Range(0, n) + .GetListSequences() + .Select(x => new object[] { x, n })); + + [Theory] + [MemberData(nameof(GetSourceSequences))] + public void TagFirstLastVaryingLengths(IDisposableEnumerable seq, int n) { - using var sequence = TestingSequence.Of(123); - - var result = sequence.TagFirstLast((item, isFirst, isLast) => new { Item = item, IsFirst = isFirst, IsLast = isLast }); - result.AssertSequenceEqual(new { Item = 123, IsFirst = true, IsLast = true }); - } - - [Fact] - public void TagFirstLastWithSourceSequenceOfTwo() - { - using var sequence = TestingSequence.Of(123, 456); - - var result = sequence.TagFirstLast((item, isFirst, isLast) => new { Item = item, IsFirst = isFirst, IsLast = isLast }); - result.AssertSequenceEqual( - new { Item = 123, IsFirst = true, IsLast = false }, - new { Item = 456, IsFirst = false, IsLast = true }); + using (seq) + { + var result = seq.TagFirstLast(); + result.AssertSequenceEqual( + Enumerable.Range(0, n) + .Select(x => (x, x == 0, x == n - 1))); + } } [Fact] - public void TagFirstLastWithSourceSequenceOfThree() + public void TagFirstLastListBehavior() { - using var sequence = TestingSequence.Of(123, 456, 789); + using var seq = Enumerable.Range(0, 10_000).AsBreakingList(); - var result = sequence.TagFirstLast((item, isFirst, isLast) => new { Item = item, IsFirst = isFirst, IsLast = isLast }); - result.AssertSequenceEqual( - new { Item = 123, IsFirst = true, IsLast = false }, - new { Item = 456, IsFirst = false, IsLast = false }, - new { Item = 789, IsFirst = false, IsLast = true }); + var result = seq.TagFirstLast(); + Assert.Equal(10_000, result.Count()); + Assert.Equal((0, true, false), result.ElementAt(0)); + Assert.Equal((30, false, false), result.ElementAt(30)); + Assert.Equal((9_999, false, true), result.ElementAt(^1)); } }