Add more LINQ-esque methods

* IEnumerable<T>.CountWhereNot(Func<T, bool>)
* IEnumerable<T>.FirstWhereNot(Func<T, bool>)
* IEnumerable<T>.FirstWhereNotOrDefault(Func<T, bool>)
* IEnumerable<T>.LastWhereNot(Func<T, bool>)
* IEnumerable<T>.LastWhereNotOrDefault(Func<T, bool>)
* IEnumerable<T>.WhereNot(Func<T, bool>)
This commit is contained in:
Oliver Booth 2022-07-30 23:53:35 +01:00
parent 580f044511
commit 3847d53120
No known key found for this signature in database
GPG Key ID: 32A00B35503AF634
3 changed files with 381 additions and 0 deletions

View File

@ -11,6 +11,12 @@
- X10D: Added `Color.GetClosestConsoleColor()` - X10D: Added `Color.GetClosestConsoleColor()`
- X10D: Added `DateTime.GetIso8601WeekOfYear()` and `DateTimeOffset.GetIso8601WeekOfYear()` - X10D: Added `DateTime.GetIso8601WeekOfYear()` and `DateTimeOffset.GetIso8601WeekOfYear()`
- X10D: Added `DirectoryInfo.Clear([bool])` - X10D: Added `DirectoryInfo.Clear([bool])`
- X10D: Added `IEnumerable<T>.CountWhereNot(Func<T, bool>)`
- X10D: Added `IEnumerable<T>.FirstWhereNot(Func<T, bool>)`
- X10D: Added `IEnumerable<T>.FirstWhereNotOrDefault(Func<T, bool>)`
- X10D: Added `IEnumerable<T>.LastWhereNot(Func<T, bool>)`
- X10D: Added `IEnumerable<T>.LastWhereNotOrDefault(Func<T, bool>)`
- X10D: Added `IEnumerable<T>.WhereNot(Func<T, bool>)`
- X10D: Added `IList<T>.RemoveRange(Range)` - X10D: Added `IList<T>.RemoveRange(Range)`
- X10D: Added `IList<T>.Swap(IList<T>)` (#62) - X10D: Added `IList<T>.Swap(IList<T>)` (#62)
- X10D: Added `Point.IsOnLine(LineF)`, `Point.IsOnLine(PointF, PointF)`, and `Point.IsOnLine(Vector2, Vector2)` - X10D: Added `Point.IsOnLine(LineF)`, `Point.IsOnLine(PointF, PointF)`, and `Point.IsOnLine(Vector2, Vector2)`

View File

@ -1,11 +1,40 @@
using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting;
using X10D.Collections; using X10D.Collections;
using X10D.Core;
namespace X10D.Tests.Collections; namespace X10D.Tests.Collections;
[TestClass] [TestClass]
public class EnumerableTests public class EnumerableTests
{ {
[TestMethod]
public void CountWhereNot_ShouldReturnCorrectCount_GivenSequence()
{
var enumerable = new[] {2, 4, 6, 7, 8, 9, 10};
int count = enumerable.CountWhereNot(x => x % 2 == 0);
Assert.AreEqual(2, count);
}
[TestMethod]
public void CountWhereNot_ShouldThrowArgumentNullException_GivenNullSource()
{
Assert.ThrowsException<ArgumentNullException>(() => ((IEnumerable<int>?)null)!.CountWhereNot(x => x % 2 == 0));
}
[TestMethod]
public void CountWhereNot_ShouldThrowOverflowException_GivenLargeSource()
{
IEnumerable<byte> GetValues()
{
while (true)
{
yield return 1;
}
}
Assert.ThrowsException<OverflowException>(() => GetValues().CountWhereNot(x => x % 2 == 0));
}
[TestMethod] [TestMethod]
public void DisposeAll_ShouldDispose_GivenCollection() public void DisposeAll_ShouldDispose_GivenCollection()
{ {
@ -36,6 +65,72 @@ public class EnumerableTests
await Assert.ThrowsExceptionAsync<ArgumentNullException>(async () => await collection!.DisposeAllAsync()); await Assert.ThrowsExceptionAsync<ArgumentNullException>(async () => await collection!.DisposeAllAsync());
} }
[TestMethod]
public void FirstWhereNot_ShouldReturnCorrectElements_GivenSequence()
{
var enumerable = new[] {2, 4, 6, 7, 8, 9, 10};
int result = enumerable.FirstWhereNot(x => x % 2 == 0);
Assert.AreEqual(7, result);
}
[TestMethod]
public void FirstWhereNot_ShouldThrowArgumentNullException_GivenNullSource()
{
Assert.ThrowsException<ArgumentNullException>(() => ((IEnumerable<int>?)null)!.FirstWhereNot(x => x % 2 == 0));
}
[TestMethod]
public void FirstWhereNot_ShouldThrowArgumentNullException_GivenNullPredicate()
{
Assert.ThrowsException<ArgumentNullException>(() => Array.Empty<int>().FirstWhereNotOrDefault(null!));
}
[TestMethod]
public void FirstWhereNot_ShouldThrowInvalidOperationException_GivenEmptySource()
{
Assert.ThrowsException<InvalidOperationException>(() => Array.Empty<int>().FirstWhereNot(x => x % 2 == 0));
}
[TestMethod]
public void FirstWhereNot_ShouldThrowInvalidOperationException_GivenSourceWithNoMatchingElements()
{
Assert.ThrowsException<InvalidOperationException>(() => 2.AsArrayValue().FirstWhereNot(x => x % 2 == 0));
}
[TestMethod]
public void FirstWhereNotOrDefault_ShouldReturnCorrectElements_GivenSequence()
{
var enumerable = new[] {2, 4, 6, 7, 8, 9, 10};
int result = enumerable.FirstWhereNotOrDefault(x => x % 2 == 0);
Assert.AreEqual(7, result);
}
[TestMethod]
public void FirstWhereNotOrDefault_ShouldThrowArgumentNullException_GivenNullSource()
{
Assert.ThrowsException<ArgumentNullException>(() => ((IEnumerable<int>?)null)!.FirstWhereNotOrDefault(x => x % 2 == 0));
}
[TestMethod]
public void FirstWhereNotOrDefault_ShouldThrowArgumentNullException_GivenNullPredicate()
{
Assert.ThrowsException<ArgumentNullException>(() => Array.Empty<int>().FirstWhereNotOrDefault(null!));
}
[TestMethod]
public void FirstWhereNotOrDefault_ShouldReturnDefault_GivenEmptySource()
{
int result = Array.Empty<int>().FirstWhereNotOrDefault(x => x % 2 == 0);
Assert.AreEqual(default, result);
}
[TestMethod]
public void FirstWhereNotOrDefault_ShouldReturnDefault_GivenSourceWithNoMatchingElements()
{
int result = 2.AsArrayValue().FirstWhereNotOrDefault(x => x % 2 == 0);
Assert.AreEqual(default, result);
}
[TestMethod] [TestMethod]
public void For_ShouldTransform_GivenTransformationDelegate() public void For_ShouldTransform_GivenTransformationDelegate()
{ {
@ -94,6 +189,72 @@ public class EnumerableTests
Assert.ThrowsException<ArgumentNullException>(() => source.ForEach(null!)); Assert.ThrowsException<ArgumentNullException>(() => source.ForEach(null!));
} }
[TestMethod]
public void LastWhereNot_ShouldReturnCorrectElements_GivenSequence()
{
var enumerable = new[] {2, 4, 6, 7, 8, 9, 10};
int result = enumerable.LastWhereNot(x => x % 2 == 0);
Assert.AreEqual(9, result);
}
[TestMethod]
public void LastWhereNot_ShouldThrowArgumentNullException_GivenNullSource()
{
Assert.ThrowsException<ArgumentNullException>(() => ((IEnumerable<int>?)null)!.LastWhereNot(x => x % 2 == 0));
}
[TestMethod]
public void LastWhereNot_ShouldThrowArgumentNullException_GivenNullPredicate()
{
Assert.ThrowsException<ArgumentNullException>(() => Array.Empty<int>().LastWhereNot(null!));
}
[TestMethod]
public void LastWhereNot_ShouldThrowInvalidOperationException_GivenEmptySource()
{
Assert.ThrowsException<InvalidOperationException>(() => Array.Empty<int>().LastWhereNot(x => x % 2 == 0));
}
[TestMethod]
public void LastWhereNot_ShouldThrowInvalidOperationException_GivenSourceWithNoMatchingElements()
{
Assert.ThrowsException<InvalidOperationException>(() => 2.AsArrayValue().LastWhereNot(x => x % 2 == 0));
}
[TestMethod]
public void LastWhereNotOrDefault_ShouldReturnCorrectElements_GivenSequence()
{
var enumerable = new[] {2, 4, 6, 7, 8, 9, 10};
int result = enumerable.LastWhereNotOrDefault(x => x % 2 == 0);
Assert.AreEqual(9, result);
}
[TestMethod]
public void LastWhereNotOrDefault_ShouldThrowArgumentNullException_GivenNullSource()
{
Assert.ThrowsException<ArgumentNullException>(() => ((IEnumerable<int>?)null)!.LastWhereNotOrDefault(x => x % 2 == 0));
}
[TestMethod]
public void LastWhereNotOrDefault_ShouldThrowArgumentNullException_GivenNullPredicate()
{
Assert.ThrowsException<ArgumentNullException>(() => Array.Empty<int>().LastWhereNotOrDefault(null!));
}
[TestMethod]
public void LastWhereNotOrDefault_ShouldReturnDefault_GivenEmptySource()
{
int result = Array.Empty<int>().LastWhereNotOrDefault(x => x % 2 == 0);
Assert.AreEqual(default, result);
}
[TestMethod]
public void LastWhereNotOrDefault_ShouldReturnDefault_GivenSourceWithNoMatchingElements()
{
int result = 2.AsArrayValue().LastWhereNotOrDefault(x => x % 2 == 0);
Assert.AreEqual(default, result);
}
[TestMethod] [TestMethod]
public void Shuffled_ShouldThrow_GivenNull() public void Shuffled_ShouldThrow_GivenNull()
{ {
@ -112,6 +273,20 @@ public class EnumerableTests
CollectionAssert.AreNotEqual(array, shuffled); CollectionAssert.AreNotEqual(array, shuffled);
} }
[TestMethod]
public void WhereNot_ShouldReturnCorrectElements_GivenSequence()
{
var enumerable = new[] {2, 4, 6, 7, 8, 9, 10};
IEnumerable<int> result = enumerable.WhereNot(x => x % 2 == 0);
CollectionAssert.AreEqual(new[] {7, 9}, result.ToArray());
}
[TestMethod]
public void WhereNot_ShouldThrowArgumentNullException_GivenNullSource()
{
Assert.ThrowsException<ArgumentNullException>(() => ((IEnumerable<int>?)null)!.WhereNot(x => x % 2 == 0));
}
private class DummyClass private class DummyClass
{ {
public int Value { get; set; } public int Value { get; set; }

View File

@ -7,6 +7,108 @@ namespace X10D.Collections;
/// </summary> /// </summary>
public static class EnumerableExtensions public static class EnumerableExtensions
{ {
/// <summary>
/// Returns a number that represents how many elements in the specified sequence do not satisfy a condition.
/// </summary>
/// <param name="source">A sequence that contains elements to be tested and counted.</param>
/// <param name="predicate">A function to test each element for a condition.</param>
/// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
/// <returns>
/// A number that represents how many elements in the sequence do not satisfy the condition in the
/// <paramref name="predicate" /> function.
/// </returns>
/// <exception cref="ArgumentNullException"><paramref name="source" /> or <paramref name="predicate" /> is null.</exception>
/// <exception cref="OverflowException">
/// The number of elements in <paramref name="source" /> is larger than <see cref="int.MaxValue" />.
/// </exception>
[Pure]
public static int CountWhereNot<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
#if NET6_0
ArgumentNullException.ThrowIfNull(source);
ArgumentNullException.ThrowIfNull(predicate);
#else
if (source is null)
{
throw new ArgumentNullException(nameof(source));
}
if (predicate is null)
{
throw new ArgumentNullException(nameof(predicate));
}
#endif
return source.Count(item => !predicate(item));
}
/// <summary>
/// Returns the first element in a sequence that does not satisfy a specified condition.
/// </summary>
/// <param name="source">An <see cref="IEnumerable{T}" /> to return an element from.</param>
/// <param name="predicate">A function to test each element for a condition.</param>
/// <typeparam name="TSource">The type of the elements in <paramref name="source" /></typeparam>
/// <returns>The first element in the sequence that fails the test in the specified predicate function.</returns>
/// <exception cref="ArgumentNullException"><paramref name="source" /> or <paramref name="predicate" /> is null.</exception>
/// <exception cref="InvalidOperationException">
/// <para>No element satisfies the condition in predicate.</para>
/// -or-
/// <para>The source sequence is empty.</para>
/// </exception>
[Pure]
public static TSource FirstWhereNot<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
#if NET6_0
ArgumentNullException.ThrowIfNull(source);
ArgumentNullException.ThrowIfNull(predicate);
#else
if (source is null)
{
throw new ArgumentNullException(nameof(source));
}
if (predicate is null)
{
throw new ArgumentNullException(nameof(predicate));
}
#endif
return source.First(item => !predicate(item));
}
/// <summary>
/// Returns the first element in a sequence that does not satisfy a specified condition.
/// </summary>
/// <param name="source">An <see cref="IEnumerable{T}" /> to return an element from.</param>
/// <param name="predicate">A function to test each element for a condition.</param>
/// <typeparam name="TSource">The type of the elements in <paramref name="source" /></typeparam>
/// <returns>
/// <see langword="default(TSource)" /> if <paramref name="source" /> is empty or if no element passes the test specified
/// by <paramref name="predicate"/>; otherwise, the first element in <paramref name="source" /> that fails the test
/// specified by <paramref name="predicate" />.
/// </returns>
/// <exception cref="ArgumentNullException"><paramref name="source" /> or <paramref name="predicate" /> is null.</exception>
[Pure]
public static TSource? FirstWhereNotOrDefault<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
#if NET6_0
ArgumentNullException.ThrowIfNull(source);
ArgumentNullException.ThrowIfNull(predicate);
#else
if (source is null)
{
throw new ArgumentNullException(nameof(source));
}
if (predicate is null)
{
throw new ArgumentNullException(nameof(predicate));
}
#endif
return source.FirstOrDefault(item => !predicate(item));
}
/// <summary> /// <summary>
/// Performs the specified action on each element of the <see cref="IEnumerable{T}" />. /// Performs the specified action on each element of the <see cref="IEnumerable{T}" />.
/// </summary> /// </summary>
@ -128,6 +230,73 @@ public static class EnumerableExtensions
} }
} }
/// <summary>
/// Returns the last element in a sequence that does not satisfy a specified condition.
/// </summary>
/// <param name="source">An <see cref="IEnumerable{T}" /> to return an element from.</param>
/// <param name="predicate">A function to test each element for a condition.</param>
/// <typeparam name="TSource">The type of the elements in <paramref name="source" /></typeparam>
/// <returns>The last element in the sequence that fails the test in the specified predicate function.</returns>
/// <exception cref="ArgumentNullException"><paramref name="source" /> or <paramref name="predicate" /> is null.</exception>
/// <exception cref="InvalidOperationException">
/// <para>No element satisfies the condition in predicate.</para>
/// -or-
/// <para>The source sequence is empty.</para>
/// </exception>
[Pure]
public static TSource LastWhereNot<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
#if NET6_0
ArgumentNullException.ThrowIfNull(source);
ArgumentNullException.ThrowIfNull(predicate);
#else
if (source is null)
{
throw new ArgumentNullException(nameof(source));
}
if (predicate is null)
{
throw new ArgumentNullException(nameof(predicate));
}
#endif
return source.Last(item => !predicate(item));
}
/// <summary>
/// Returns the last element in a sequence that does not satisfy a specified condition.
/// </summary>
/// <param name="source">An <see cref="IEnumerable{T}" /> to return an element from.</param>
/// <param name="predicate">A function to test each element for a condition.</param>
/// <typeparam name="TSource">The type of the elements in <paramref name="source" /></typeparam>
/// <returns>
/// <see langword="default(TSource)" /> if <paramref name="source" /> is empty or if no element passes the test specified
/// by <paramref name="predicate"/>; otherwise, the last element in <paramref name="source" /> that fails the test
/// specified by <paramref name="predicate" />.
/// </returns>
/// <exception cref="ArgumentNullException"><paramref name="source" /> or <paramref name="predicate" /> is null.</exception>
[Pure]
public static TSource? LastWhereNotOrDefault<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
#if NET6_0
ArgumentNullException.ThrowIfNull(source);
ArgumentNullException.ThrowIfNull(predicate);
#else
if (source is null)
{
throw new ArgumentNullException(nameof(source));
}
if (predicate is null)
{
throw new ArgumentNullException(nameof(predicate));
}
#endif
return source.LastOrDefault(item => !predicate(item));
}
/// <summary> /// <summary>
/// Reorganizes the elements in an enumerable by implementing a Fisher-Yates shuffle, and returns th shuffled result. /// Reorganizes the elements in an enumerable by implementing a Fisher-Yates shuffle, and returns th shuffled result.
/// </summary> /// </summary>
@ -152,4 +321,35 @@ public static class EnumerableExtensions
list.Shuffle(random); list.Shuffle(random);
return list.AsReadOnly(); return list.AsReadOnly();
} }
/// <summary>
/// Filters a sequence of values based on a predicate, such that all elements in the result do not match the predicate.
/// </summary>
/// <param name="source">An <see cref="IEnumerable{T}" /> to filter.</param>
/// <param name="predicate">A function to test each element for a condition.</param>
/// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
/// <returns>
/// An <see cref="IEnumerable{T}" /> that contains elements from the input sequence that do not satisfy the condition.
/// </returns>
/// <exception cref="ArgumentNullException"><paramref name="source" /> or <paramref name="predicate" /> is null.</exception>
[Pure]
public static IEnumerable<TSource> WhereNot<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
#if NET6_0
ArgumentNullException.ThrowIfNull(source);
ArgumentNullException.ThrowIfNull(predicate);
#else
if (source is null)
{
throw new ArgumentNullException(nameof(source));
}
if (predicate is null)
{
throw new ArgumentNullException(nameof(predicate));
}
#endif
return source.Where(item => !predicate(item));
}
} }