From f175221a6bc2daf56a63224b168c0daba53786aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tau=20G=C3=A4rtli?= Date: Sun, 9 Mar 2025 13:19:20 +0100 Subject: [PATCH 1/3] Add WhereSelect with index overloads --- .../AsyncEnumerableExtensions/WhereSelect.cs | 28 +++++++++++++++++-- Funcky.Async/PublicAPI.Unshipped.txt | 3 ++ .../EnumerableExtensions/WhereSelect.cs | 8 +++++- Funcky/PublicAPI.Unshipped.txt | 1 + 4 files changed, 36 insertions(+), 4 deletions(-) diff --git a/Funcky.Async/Extensions/AsyncEnumerableExtensions/WhereSelect.cs b/Funcky.Async/Extensions/AsyncEnumerableExtensions/WhereSelect.cs index 4e216d6b..4acfabb1 100644 --- a/Funcky.Async/Extensions/AsyncEnumerableExtensions/WhereSelect.cs +++ b/Funcky.Async/Extensions/AsyncEnumerableExtensions/WhereSelect.cs @@ -3,7 +3,7 @@ namespace Funcky.Extensions; public static partial class AsyncEnumerableExtensions { /// - /// Filters out all the empty values from an IEnumerable<Option<T>> and therefore returns an . + /// Filters out all the empty values from an >]]> and therefore returns an . /// [Pure] public static IAsyncEnumerable WhereSelect(this IAsyncEnumerable> source) @@ -19,18 +19,40 @@ public static IAsyncEnumerable WhereSelect(this IAsyn where TResult : notnull => source.Select(selector).SelectMany(ToAsyncEnumerable); - /// + /// + /// Projects and filters an at the same time. + /// This is done by filtering out any empty values returned by the . + /// The index of each source element is used in the projected form of that element. + /// + [Pure] + public static IAsyncEnumerable WhereSelect(this IAsyncEnumerable source, Func> selector) + where TResult : notnull + => source.Select(selector).SelectMany(ToAsyncEnumerable); + + /// [Pure] public static IAsyncEnumerable WhereSelectAwait(this IAsyncEnumerable source, Func>> selector) where TResult : notnull => source.SelectAwait(selector).SelectMany(ToAsyncEnumerable); - /// + /// + [Pure] + public static IAsyncEnumerable WhereSelectAwait(this IAsyncEnumerable source, Func>> selector) + where TResult : notnull + => source.SelectAwait(selector).SelectMany(ToAsyncEnumerable); + + /// [Pure] public static IAsyncEnumerable WhereSelectAwaitWithCancellation(this IAsyncEnumerable source, Func>> selector) where TResult : notnull => source.SelectAwaitWithCancellation(selector).SelectMany(ToAsyncEnumerable); + /// + [Pure] + public static IAsyncEnumerable WhereSelectAwaitWithCancellation(this IAsyncEnumerable source, Func>> selector) + where TResult : notnull + => source.SelectAwaitWithCancellation(selector).SelectMany(ToAsyncEnumerable); + private static IAsyncEnumerable ToAsyncEnumerable(Option option) where TItem : notnull => option.Match( diff --git a/Funcky.Async/PublicAPI.Unshipped.txt b/Funcky.Async/PublicAPI.Unshipped.txt index 13426eba..67279dda 100644 --- a/Funcky.Async/PublicAPI.Unshipped.txt +++ b/Funcky.Async/PublicAPI.Unshipped.txt @@ -1,2 +1,5 @@ #nullable enable static Funcky.Extensions.AsyncEnumerableExtensions.Traverse(this System.Collections.Generic.IAsyncEnumerable! source, System.Func!>! selector) -> System.Lazy!>! +static Funcky.Extensions.AsyncEnumerableExtensions.WhereSelect(this System.Collections.Generic.IAsyncEnumerable! source, System.Func>! selector) -> System.Collections.Generic.IAsyncEnumerable! +static Funcky.Extensions.AsyncEnumerableExtensions.WhereSelectAwait(this System.Collections.Generic.IAsyncEnumerable! source, System.Func>>! selector) -> System.Collections.Generic.IAsyncEnumerable! +static Funcky.Extensions.AsyncEnumerableExtensions.WhereSelectAwaitWithCancellation(this System.Collections.Generic.IAsyncEnumerable! source, System.Func>>! selector) -> System.Collections.Generic.IAsyncEnumerable! diff --git a/Funcky/Extensions/EnumerableExtensions/WhereSelect.cs b/Funcky/Extensions/EnumerableExtensions/WhereSelect.cs index 04450f43..42307792 100644 --- a/Funcky/Extensions/EnumerableExtensions/WhereSelect.cs +++ b/Funcky/Extensions/EnumerableExtensions/WhereSelect.cs @@ -3,7 +3,7 @@ namespace Funcky.Extensions; public static partial class EnumerableExtensions { /// - /// Filters out all the empty values from an IEnumerable<Option<T>> and therefore returns an . + /// Filters out all the empty values from an >]]> and therefore returns an . /// [Pure] public static IEnumerable WhereSelect(this IEnumerable> source) @@ -18,4 +18,10 @@ public static IEnumerable WhereSelect(this IEnumerable WhereSelect(this IEnumerable source, Func> selector) where TResult : notnull => source.SelectMany(input => selector(input).ToEnumerable()); + + /// + [Pure] + public static IEnumerable WhereSelect(this IEnumerable source, Func> selector) + where TResult : notnull + => source.SelectMany((input, index) => selector(input, index).ToEnumerable()); } diff --git a/Funcky/PublicAPI.Unshipped.txt b/Funcky/PublicAPI.Unshipped.txt index 151a8982..52871fca 100644 --- a/Funcky/PublicAPI.Unshipped.txt +++ b/Funcky/PublicAPI.Unshipped.txt @@ -5,6 +5,7 @@ Funcky.Monads.IEither Funcky.Monads.IOption static Funcky.Extensions.DictionaryExtensions.RemoveOrNone(this System.Collections.Generic.IDictionary! dictionary, TKey key) -> Funcky.Monads.Option static Funcky.Extensions.EnumerableExtensions.Traverse(this System.Collections.Generic.IEnumerable! source, System.Func!>! selector) -> System.Lazy!>! +static Funcky.Extensions.EnumerableExtensions.WhereSelect(this System.Collections.Generic.IEnumerable! source, System.Func>! selector) -> System.Collections.Generic.IEnumerable! static Funcky.Extensions.FuncExtensions.Apply(this System.Func! func, Funcky.Unit p1, Funcky.Unit p2, Funcky.Unit p3, Funcky.Unit p4, T5 p5) -> System.Func! static Funcky.Extensions.FuncExtensions.Apply(this System.Func! func, Funcky.Unit p1, Funcky.Unit p2, Funcky.Unit p3, T4 p4, Funcky.Unit p5) -> System.Func! static Funcky.Extensions.FuncExtensions.Apply(this System.Func! func, Funcky.Unit p1, Funcky.Unit p2, Funcky.Unit p3, T4 p4, T5 p5) -> System.Func! From 2e8de0f2ea590ac855edfc1fcb12a7907f3fa1a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tau=20G=C3=A4rtli?= Date: Sun, 9 Mar 2025 13:20:00 +0100 Subject: [PATCH 2/3] Re-use `.ToAsyncEnumerable()` extension --- .../Extensions/AsyncEnumerableExtensions/WhereSelect.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Funcky.Async/Extensions/AsyncEnumerableExtensions/WhereSelect.cs b/Funcky.Async/Extensions/AsyncEnumerableExtensions/WhereSelect.cs index 4acfabb1..a3e21bc6 100644 --- a/Funcky.Async/Extensions/AsyncEnumerableExtensions/WhereSelect.cs +++ b/Funcky.Async/Extensions/AsyncEnumerableExtensions/WhereSelect.cs @@ -55,7 +55,5 @@ public static IAsyncEnumerable WhereSelectAwaitWithCancellation ToAsyncEnumerable(Option option) where TItem : notnull - => option.Match( - none: AsyncEnumerable.Empty, - some: AsyncSequence.Return); + => option.ToAsyncEnumerable(); } From 159093b9975f12f360066f8cda70e5f9114719df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tau=20G=C3=A4rtli?= Date: Sun, 9 Mar 2025 13:25:18 +0100 Subject: [PATCH 3/3] Add tests for new overload --- .../AsyncEnumerableExtensions/WhereSelectTest.cs | 12 ++++++++++++ .../EnumerableExtensions/WhereSelectTest.cs | 11 +++++++++++ 2 files changed, 23 insertions(+) diff --git a/Funcky.Async.Test/Extensions/AsyncEnumerableExtensions/WhereSelectTest.cs b/Funcky.Async.Test/Extensions/AsyncEnumerableExtensions/WhereSelectTest.cs index 440b1d0d..54456773 100644 --- a/Funcky.Async.Test/Extensions/AsyncEnumerableExtensions/WhereSelectTest.cs +++ b/Funcky.Async.Test/Extensions/AsyncEnumerableExtensions/WhereSelectTest.cs @@ -1,3 +1,5 @@ +using static Funcky.Discard; + namespace Funcky.Async.Test.Extensions.AsyncEnumerableExtensions; public sealed class WhereSelectTest @@ -10,6 +12,16 @@ public async Task WhereSelectRetainsOnlySomeValues() Assert.Equal(expectedSquares, squares); } + [Fact] + public async Task WhereSelectReceivesTheSourceElementsIndex() + { + const int count = 6; + var expectedSequence = Enumerable.Range(0, count: count); + var units = AsyncSequence.Cycle(__).Take(count); + var indexes = units.WhereSelect((_, index) => Option.Some(index)); + Assert.Equal(expectedSequence, await indexes.ToListAsync()); + } + private static Option SquareEvenNumbers(int n) => n % 2 == 0 ? n * n diff --git a/Funcky.Test/Extensions/EnumerableExtensions/WhereSelectTest.cs b/Funcky.Test/Extensions/EnumerableExtensions/WhereSelectTest.cs index 2dc907a3..a65f8023 100644 --- a/Funcky.Test/Extensions/EnumerableExtensions/WhereSelectTest.cs +++ b/Funcky.Test/Extensions/EnumerableExtensions/WhereSelectTest.cs @@ -1,4 +1,5 @@ using Funcky.Test.TestUtils; +using static Funcky.Discard; namespace Funcky.Test.Extensions.EnumerableExtensions; @@ -21,6 +22,16 @@ public void WhereSelectFiltersEmptySelectorValues() Assert.Equal(expectedResult, result); } + [Fact] + public void WhereSelectReceivesTheSourceElementsIndex() + { + const int count = 6; + var expectedSequence = Enumerable.Range(0, count: count); + var units = Sequence.Cycle(__).Take(count); + var indexes = units.WhereSelect((_, index) => Option.Some(index)); + Assert.Equal(expectedSequence, indexes); + } + [Fact] public void WhereSelectFiltersEmptyFromSequence() {