-
Notifications
You must be signed in to change notification settings - Fork 416
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This is a squashed merge of PR #1085 that closes #1036. --------- Co-authored-by: Atif Aziz <code@raboof.com>
- Loading branch information
1 parent
72c3c66
commit cf2b725
Showing
11 changed files
with
236 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
#region License and Terms | ||
// MoreLINQ - Extensions to LINQ to Objects | ||
// Copyright (c) 2024 Andy Romero (armorynode). 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 NUnit.Framework; | ||
|
||
[TestFixture] | ||
public class SkipLastWhileTest | ||
{ | ||
[Test] | ||
public void IsLazy() | ||
{ | ||
_ = new BreakingSequence<object>().SkipLastWhile(BreakingFunc.Of<object, bool>()); | ||
} | ||
|
||
[TestCase(SourceKind.Sequence)] | ||
[TestCase(SourceKind.BreakingList)] | ||
[TestCase(SourceKind.BreakingReadOnlyList)] | ||
public void PredicateNeverFalse(SourceKind sourceKind) | ||
{ | ||
using var sequence = TestingSequence.Of(0, 1, 2, 3, 4); | ||
|
||
Assert.That(sequence.ToSourceKind(sourceKind).SkipLastWhile(x => x < 5), Is.Empty); | ||
} | ||
|
||
[TestCase(SourceKind.Sequence)] | ||
[TestCase(SourceKind.BreakingList)] | ||
[TestCase(SourceKind.BreakingReadOnlyList)] | ||
public void PredicateNeverTrue(SourceKind sourceKind) | ||
{ | ||
using var sequence = TestingSequence.Of(0, 1, 2, 3, 4); | ||
|
||
sequence.ToSourceKind(sourceKind) | ||
.SkipLastWhile(x => x == 100) | ||
.AssertSequenceEqual(0, 1, 2, 3, 4); | ||
} | ||
|
||
[TestCase(SourceKind.Sequence)] | ||
[TestCase(SourceKind.BreakingList)] | ||
[TestCase(SourceKind.BreakingReadOnlyList)] | ||
public void PredicateBecomesTruePartWay(SourceKind sourceKind) | ||
{ | ||
using var sequence = TestingSequence.Of(0, 1, 2, 3, 4); | ||
|
||
sequence.ToSourceKind(sourceKind) | ||
.SkipLastWhile(x => x > 2) | ||
.AssertSequenceEqual(0, 1, 2); | ||
} | ||
|
||
[TestCase(SourceKind.Sequence)] | ||
[TestCase(SourceKind.BreakingList)] | ||
[TestCase(SourceKind.BreakingReadOnlyList)] | ||
public void NeverEvaluatesPredicateWhenSourceIsEmpty(SourceKind sourceKind) | ||
{ | ||
using var sequence = TestingSequence.Of<int>(); | ||
|
||
Assert.That(sequence.ToSourceKind(sourceKind) | ||
.SkipLastWhile(BreakingFunc.Of<int, bool>()), | ||
Is.Empty); | ||
} | ||
|
||
[TestCase(SourceKind.Sequence)] | ||
[TestCase(SourceKind.BreakingList)] | ||
[TestCase(SourceKind.BreakingReadOnlyList)] | ||
public void UsesCollectionCountAtIterationTime(SourceKind sourceKind) | ||
{ | ||
var list = new List<int> { 1, 2, 3, 4 }; | ||
var result = list.ToSourceKind(sourceKind).SkipLastWhile(x => x > 2); | ||
list.Add(5); | ||
result.AssertSequenceEqual(1, 2); | ||
} | ||
|
||
[TestCase(SourceKind.Sequence)] | ||
[TestCase(SourceKind.BreakingList)] | ||
[TestCase(SourceKind.BreakingReadOnlyList)] | ||
public void KeepsNonTrailingItemsThatMatchPredicate(SourceKind sourceKind) | ||
{ | ||
using var sequence = TestingSequence.Of(1, 2, 0, 0, 3, 4, 0, 0); | ||
|
||
sequence.ToSourceKind(sourceKind) | ||
.SkipLastWhile(x => x == 0) | ||
.AssertSequenceEqual(1, 2, 0, 0, 3, 4); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -86,6 +86,7 @@ | |
- Sequence | ||
- Shuffle | ||
- SkipLast | ||
- SkipLastWhile | ||
- SkipUntil | ||
- Slice | ||
- SortedMerge | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,4 @@ | ||
#nullable enable | ||
MoreLinq.Extensions.SkipLastWhileExtension | ||
static MoreLinq.Extensions.SkipLastWhileExtension.SkipLastWhile<T>(this System.Collections.Generic.IEnumerable<T>! source, System.Func<T, bool>! predicate) -> System.Collections.Generic.IEnumerable<T>! | ||
static MoreLinq.MoreEnumerable.SkipLastWhile<T>(this System.Collections.Generic.IEnumerable<T>! source, System.Func<T, bool>! predicate) -> System.Collections.Generic.IEnumerable<T>! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,7 @@ | ||
#nullable enable | ||
MoreLinq.Extensions.SkipLastWhileExtension | ||
static MoreLinq.Extensions.SkipLastWhileExtension.SkipLastWhile<T>(this System.Collections.Generic.IEnumerable<T>! source, System.Func<T, bool>! predicate) -> System.Collections.Generic.IEnumerable<T>! | ||
static MoreLinq.MoreEnumerable.Sequence<T>(T start) -> System.Collections.Generic.IEnumerable<T>! | ||
static MoreLinq.MoreEnumerable.Sequence<T>(T start, T stop) -> System.Collections.Generic.IEnumerable<T>! | ||
static MoreLinq.MoreEnumerable.Sequence<T>(T start, T stop, T step) -> System.Collections.Generic.IEnumerable<T>! | ||
static MoreLinq.MoreEnumerable.SkipLastWhile<T>(this System.Collections.Generic.IEnumerable<T>! source, System.Func<T, bool>! predicate) -> System.Collections.Generic.IEnumerable<T>! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,4 @@ | ||
#nullable enable | ||
MoreLinq.Extensions.SkipLastWhileExtension | ||
static MoreLinq.Extensions.SkipLastWhileExtension.SkipLastWhile<T>(this System.Collections.Generic.IEnumerable<T>! source, System.Func<T, bool>! predicate) -> System.Collections.Generic.IEnumerable<T>! | ||
static MoreLinq.MoreEnumerable.SkipLastWhile<T>(this System.Collections.Generic.IEnumerable<T>! source, System.Func<T, bool>! predicate) -> System.Collections.Generic.IEnumerable<T>! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,4 @@ | ||
#nullable enable | ||
MoreLinq.Extensions.SkipLastWhileExtension | ||
static MoreLinq.Extensions.SkipLastWhileExtension.SkipLastWhile<T>(this System.Collections.Generic.IEnumerable<T>! source, System.Func<T, bool>! predicate) -> System.Collections.Generic.IEnumerable<T>! | ||
static MoreLinq.MoreEnumerable.SkipLastWhile<T>(this System.Collections.Generic.IEnumerable<T>! source, System.Func<T, bool>! predicate) -> System.Collections.Generic.IEnumerable<T>! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
#region License and Terms | ||
// MoreLINQ - Extensions to LINQ to Objects | ||
// Copyright (c) 2024 Andy Romero (armorynode). All rights reserved. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
#endregion | ||
|
||
namespace MoreLinq | ||
{ | ||
using System; | ||
using System.Collections.Generic; | ||
|
||
static partial class MoreEnumerable | ||
{ | ||
/// <summary> | ||
/// Removes elements from the end of a sequence as long as a specified condition is true. | ||
/// </summary> | ||
/// <typeparam name="T">Type of the source sequence.</typeparam> | ||
/// <param name="source">The source sequence.</param> | ||
/// <param name="predicate">The predicate to use to remove items from the tail of the sequence.</param> | ||
/// <returns> | ||
/// An <see cref="IEnumerable{T}"/> containing the source sequence elements except for the bypassed ones at the end. | ||
/// </returns> | ||
/// <exception cref="ArgumentNullException">The source sequence is <see langword="null"/>.</exception> | ||
/// <exception cref="ArgumentNullException">The predicate is <see langword="null"/>.</exception> | ||
/// <remarks> | ||
/// This operator uses deferred execution and streams its results. At any given time, it | ||
/// will buffer as many consecutive elements as satisfied by <paramref name="predicate"/>. | ||
/// </remarks> | ||
|
||
public static IEnumerable<T> SkipLastWhile<T>(this IEnumerable<T> source, Func<T, bool> predicate) | ||
{ | ||
if (source == null) throw new ArgumentNullException(nameof(source)); | ||
if (predicate == null) throw new ArgumentNullException(nameof(predicate)); | ||
|
||
return source.TryAsListLike() switch | ||
{ | ||
{ } list => IterateList(list, predicate), | ||
_ => IterateSequence(source, predicate), | ||
}; | ||
|
||
static IEnumerable<T> IterateList(ListLike<T> list, Func<T, bool> predicate) | ||
{ | ||
var i = list.Count - 1; | ||
while (i >= 0 && predicate(list[i])) | ||
{ | ||
i--; | ||
} | ||
|
||
for (var j = 0; j <= i; j++) | ||
{ | ||
yield return list[j]; | ||
} | ||
} | ||
|
||
static IEnumerable<T> IterateSequence(IEnumerable<T> source, Func<T, bool> predicate) | ||
{ | ||
Queue<T>? queue = null; | ||
foreach (var item in source) | ||
{ | ||
if (predicate(item)) | ||
{ | ||
queue ??= new Queue<T>(); | ||
queue.Enqueue(item); | ||
} | ||
else | ||
{ | ||
while (queue?.Count > 0) | ||
{ | ||
yield return queue.Dequeue(); | ||
} | ||
yield return item; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters