tools/code/common/Enumerable.cs (152 lines of code) (raw):
using LanguageExt;
using LanguageExt.UnsafeValueAccess;
using Nito.Comparers;
using System;
using System.Collections;
using System.Collections.Frozen;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace common;
public static class IEnumerableExtensions
{
/// <summary>
/// Iterates over an <seealso cref="IEnumerable{T}"/> and executes <paramref name="action"/> for each element.
/// Each action is executed sequentially. The function returns after all actions have executed.
/// </summary>
public static void Iter<T>(this IEnumerable<T> enumerable, Action<T> action)
{
foreach (var t in enumerable)
{
action(t);
}
}
/// <summary>
/// Iterates over an <seealso cref="IEnumerable{T}"/> and executes <paramref name="action"/> for each element.
/// Each action is executed sequentially. The function returns after all actions have executed.
/// </summary>
public static async ValueTask Iter<T>(this IEnumerable<T> enumerable, Func<T, ValueTask> action, CancellationToken cancellationToken) =>
await enumerable.IterParallel(async (t, _) => await action(t), maxDegreeOfParallelism: 1, cancellationToken);
/// <summary>
/// Iterates over an <seealso cref="IEnumerable{T}"/> and executes <paramref name="action"/> for each element.
/// Each action is executed in parallel. The function will wait for all actions to complete before returning.
/// </summary>
public static async ValueTask IterParallel<T>(this IEnumerable<T> enumerable, Func<T, ValueTask> action, CancellationToken cancellationToken) =>
await enumerable.IterParallel(async (t, _) => await action(t), maxDegreeOfParallelism: -1, cancellationToken);
/// <summary>
/// Iterates over an <seealso cref="IEnumerable{T}"/> and executes <paramref name="action"/> for each element.
/// Each action is executed in parallel. The function will wait for all actions to complete before returning.
/// </summary>
public static async ValueTask IterParallel<T>(this IEnumerable<T> enumerable, Func<T, CancellationToken, ValueTask> action, CancellationToken cancellationToken) =>
await enumerable.IterParallel(action, maxDegreeOfParallelism: -1, cancellationToken);
/// <summary>
/// Iterates over an <seealso cref="IEnumerable{T}"/> and executes <paramref name="action"/> for each element.
/// <paramref name="maxDegreeOfParallelism"/> controls the maximum number of parallel actions. The function will wait for all actions to complete before returning.
/// </summary>
public static async ValueTask IterParallel<T>(this IEnumerable<T> enumerable, Func<T, CancellationToken, ValueTask> action, int maxDegreeOfParallelism, CancellationToken cancellationToken)
{
var options = new ParallelOptions
{
MaxDegreeOfParallelism = maxDegreeOfParallelism,
CancellationToken = cancellationToken
};
await Parallel.ForEachAsync(enumerable, parallelOptions: options, action);
}
/// <summary>
/// Iterates over an <seealso cref="IEnumerable"/> and executes <paramref name="action"/> for each element.
/// Each action is executed in parallel. The function will wait for all actions to complete before returning.
/// </summary>
public static async ValueTask IterParallel<T1, T2>(this IEnumerable<(T1, T2)> enumerable, Func<T1, T2, CancellationToken, ValueTask> action, CancellationToken cancellationToken) =>
await enumerable.IterParallel(async (t, cancellationToken) => await action(t.Item1, t.Item2, cancellationToken), cancellationToken);
/// <summary>
/// Applies <paramref name="f"/> to each element and filters out <seealso cref="Option.None"/> values.
/// </summary>
public static IEnumerable<T2> Choose<T, T2>(this IEnumerable<T> enumerable, Func<T, Option<T2>> f)
{
var enumerableM = enumerable.AsEnumerableM();
return EnumerableMExtensions.Choose(enumerableM, f);
}
/// <summary>
/// Applies <paramref name="f"/> to each element of <paramref name="enumerable"/> and returns the first Option of <typeparamref name="T2"/>
/// that is Some. If all options are None, returns a None.
/// </summary>
public static async ValueTask<Option<T2>> Pick<T, T2>(this IEnumerable<T> enumerable, Func<T, CancellationToken, ValueTask<Option<T2>>> f, CancellationToken cancellationToken)
{
foreach (var item in enumerable)
{
var option = await f(item, cancellationToken);
if (option.IsSome)
{
return option;
}
}
return Option<T2>.None;
}
/// <summary>
/// Returns the first item in the enumerable. If the enumerable is empty, returns <seealso cref="Option.None"/>.
/// </summary>
public static Option<T> HeadOrNone<T>(this IEnumerable<T> enumerable)
{
var m = enumerable.AsEnumerableM();
return FoldableExtensions.Head(m);
}
/// <summary>
/// Returns the last item in the enumerable. If the enumerable is empty, returns <seealso cref="Option.None"/>.
/// </summary>
public static Option<T> LastOrNone<T>(this IEnumerable<T> enumerable)
{
var m = enumerable.AsEnumerableM();
return FoldableExtensions.Last(m);
}
public static FrozenSet<T> ToFrozenSet<T, TKey>(this IEnumerable<T> enumerable, Func<T, TKey> keySelector)
{
var comparer = EqualityComparerBuilder.For<T>().EquateBy(keySelector);
return enumerable.ToFrozenSet(comparer);
}
public static FrozenDictionary<TKey, TValue> ToFrozenDictionary<TKey, TValue>(this IEnumerable<(TKey, TValue)> enumerable, IEqualityComparer<TKey>? comparer = default) where TKey : notnull =>
enumerable.ToFrozenDictionary(kvp => kvp.Item1, kvp => kvp.Item2, comparer);
}
public static class IAsyncEnumerableExtensions
{
/// <summary>
/// Iterates over an <seealso cref="IEnumerable{T}"/> and executes <paramref name="action"/> for each element.
/// Each action is executed sequentially. The function returns after all actions have executed.
/// </summary>
public static async ValueTask Iter<T>(this IAsyncEnumerable<T> enumerable, Func<T, ValueTask> action, CancellationToken cancellationToken) =>
await enumerable.Iter(async (t, cancellationToken) => await action(t), cancellationToken);
/// <summary>
/// Iterates over an <seealso cref="IEnumerable{T}"/> and executes <paramref name="action"/> for each element.
/// Each action is executed sequentially. The function returns after all actions have executed.
/// </summary>
public static async ValueTask Iter<T>(this IAsyncEnumerable<T> enumerable, Func<T, CancellationToken, ValueTask> action, CancellationToken cancellationToken)
{
await foreach (var item in enumerable.WithCancellation(cancellationToken))
{
await action(item, cancellationToken);
}
}
/// <summary>
/// Iterates over an <seealso cref="IAsyncEnumerable{T}"/> and executes <paramref name="action"/> for each element.
/// Each action is executed in parallel. The function will wait for all actions to complete before returning.
/// </summary>
public static async ValueTask IterParallel<T>(this IAsyncEnumerable<T> enumerable, Func<T, ValueTask> action, CancellationToken cancellationToken) =>
await enumerable.IterParallel(async (t, _) => await action(t), maxDegreeOfParallelism: -1, cancellationToken);
/// <summary>
/// Iterates over an <seealso cref="IAsyncEnumerable{T}"/> and executes <paramref name="action"/> for each element.
/// Each action is executed in parallel. The function will wait for all actions to complete before returning.
/// </summary>
public static async ValueTask IterParallel<T>(this IAsyncEnumerable<T> enumerable, Func<T, CancellationToken, ValueTask> action, CancellationToken cancellationToken) =>
await enumerable.IterParallel(action, maxDegreeOfParallelism: -1, cancellationToken);
/// <summary>
/// Iterates over an <seealso cref="IAsyncEnumerable{T}"/> and executes <paramref name="action"/> for each element.
/// <paramref name="maxDegreeOfParallelism"/> controls the maximum number of parallel actions. The function will wait for all actions to complete before returning.
/// </summary>
public static async ValueTask IterParallel<T>(this IAsyncEnumerable<T> enumerable, Func<T, CancellationToken, ValueTask> action, int maxDegreeOfParallelism, CancellationToken cancellationToken)
{
var options = new ParallelOptions
{
MaxDegreeOfParallelism = maxDegreeOfParallelism,
CancellationToken = cancellationToken
};
await Parallel.ForEachAsync(enumerable, parallelOptions: options, action);
}
/// <summary>
/// Iterates over an <seealso cref="IAsyncEnumerable"/> and executes <paramref name="action"/> for each element.
/// Each action is executed in parallel. The function will wait for all actions to complete before returning.
/// </summary>
public static async ValueTask IterParallel<T1, T2>(this IAsyncEnumerable<(T1, T2)> enumerable, Func<T1, T2, CancellationToken, ValueTask> action, CancellationToken cancellationToken) =>
await enumerable.IterParallel(async (t, cancellationToken) => await action(t.Item1, t.Item2, cancellationToken), cancellationToken);
public static async ValueTask<FrozenSet<T>> ToFrozenSet<T>(this IAsyncEnumerable<T> enumerable, CancellationToken cancellationToken, IEqualityComparer<T>? comparer = default)
{
var items = await enumerable.ToListAsync(cancellationToken);
return items.ToFrozenSet(comparer);
}
/// <summary>
/// Applies <paramref name="f"/> to each element and filters out <seealso cref="Option.None"/> values.
/// </summary>
public static IAsyncEnumerable<T2> Choose<T, T2>(this IAsyncEnumerable<T> enumerable, Func<T, Option<T2>> f) =>
enumerable.Choose(async (t, cancellationToken) => await ValueTask.FromResult(f(t)));
/// <summary>
/// Applies <paramref name="f"/> to each element and filters out <seealso cref="Option.None"/> values.
/// </summary>
public static IAsyncEnumerable<T2> Choose<T, T2>(this IAsyncEnumerable<T> enumerable, Func<T, ValueTask<Option<T2>>> f) =>
enumerable.Choose(async (t, cancellationToken) => await f(t));
/// <summary>
/// Applies <paramref name="f"/> to each element and filters out <seealso cref="Option.None"/> values.
/// </summary>
public static IAsyncEnumerable<T2> Choose<T, T2>(this IAsyncEnumerable<T> enumerable, Func<T, CancellationToken, ValueTask<Option<T2>>> f) =>
enumerable.SelectAwaitWithCancellation(f)
.Where(option => option.IsSome)
.Select(option => option.ValueUnsafe()!);
/// <summary>
///
/// </summary>
public static async ValueTask<Option<T>> FirstOrNone<T>(this IAsyncEnumerable<T> enumerable, CancellationToken cancellationToken) =>
await enumerable.Select(Option<T>.Some)
.DefaultIfEmpty(Option<T>.None)
.FirstAsync(cancellationToken);
/// <summary>
/// Applies <paramref name="f"/> to each element of <paramref name="enumerable"/> and returns the first Option of <typeparamref name="T2"/>
/// that is Some. If all options are None, returns a None.
/// </summary>
public static async ValueTask<Option<T2>> Pick<T, T2>(this IAsyncEnumerable<T> enumerable, Func<T, CancellationToken, ValueTask<Option<T2>>> f, CancellationToken cancellationToken) =>
await enumerable.Choose(f)
.FirstOrNone(cancellationToken);
public static async ValueTask<FrozenDictionary<TKey, TValue>> ToFrozenDictionary<TKey, TValue>(this IAsyncEnumerable<(TKey, TValue)> enumerable, CancellationToken cancellationToken, IEqualityComparer<TKey>? comparer = default) where TKey : notnull
{
var array = await enumerable.ToArrayAsync(cancellationToken);
return array.ToFrozenDictionary(comparer);
}
}
public static class KeyValuePairExtensions
{
/// <summary>
/// Applies <paramref name="f"/> to each value and filters out <seealso cref="Option.None"/> values.
/// </summary>
public static IEnumerable<KeyValuePair<TKey, TValue2>> ChooseValues<TKey, TValue, TValue2>(this IEnumerable<KeyValuePair<TKey, TValue>> keyValuePairs, Func<TValue, Option<TValue2>> f) =>
keyValuePairs.Choose(kvp => from value2 in f(kvp.Value)
select KeyValuePair.Create(kvp.Key, value2));
/// <summary>
/// Applies <paramref name="f"/> to each key and filters out <seealso cref="Option.None"/> keys.
/// </summary>
public static IEnumerable<KeyValuePair<TKey2, TValue>> ChooseKeys<TKey, TKey2, TValue>(this IEnumerable<KeyValuePair<TKey, TValue>> keyValuePairs, Func<TKey, Option<TKey2>> f) =>
keyValuePairs.Choose(kvp => from key2 in f(kvp.Key)
select KeyValuePair.Create(key2, kvp.Value));
/// <summary>
/// Creates a new key value pair whose value is <paramref name="f"/>(<paramref name="keyValuePair"/>.Value).
/// </summary>
public static KeyValuePair<TKey, TValue2> MapValue<TKey, TValue, TValue2>(this KeyValuePair<TKey, TValue> keyValuePair, Func<TValue, TValue2> f) =>
KeyValuePair.Create(keyValuePair.Key, f(keyValuePair.Value));
/// <summary>
/// Creates a new key value pair whose key is <paramref name="f"/>(<paramref name="keyValuePair"/>.Key).
/// </summary>
public static KeyValuePair<TKey2, TValue> MapKey<TKey, TKey2, TValue>(this KeyValuePair<TKey, TValue> keyValuePair, Func<TKey, TKey2> f) =>
KeyValuePair.Create(f(keyValuePair.Key), keyValuePair.Value);
/// <summary>
/// Applies <paramref name="f"/> to each key
/// </summary>
public static IEnumerable<KeyValuePair<TKey2, TValue>> MapKey<TKey, TKey2, TValue>(this IEnumerable<KeyValuePair<TKey, TValue>> enumerable, Func<TKey, TKey2> f) =>
enumerable.Select(kvp => kvp.MapKey(f));
/// <summary>
/// Applies <paramref name="f"/> to each value
/// </summary>
public static IEnumerable<KeyValuePair<TKey, TValue2>> MapValue<TKey, TValue, TValue2>(this IEnumerable<KeyValuePair<TKey, TValue>> enumerable, Func<TValue, TValue2> f) =>
enumerable.Select(kvp => kvp.MapValue(f));
/// <summary>
/// Removes keys where the predicate is false
/// </summary>
public static IEnumerable<KeyValuePair<TKey, TValue>> WhereKey<TKey, TValue>(this IEnumerable<KeyValuePair<TKey, TValue>> enumerable, Func<TKey, bool> predicate) =>
enumerable.Where(kvp => predicate(kvp.Key));
/// <summary>
/// Removes values where the predicate is false
/// </summary>
public static IEnumerable<KeyValuePair<TKey, TValue>> WhereValue<TKey, TValue>(this IEnumerable<KeyValuePair<TKey, TValue>> enumerable, Func<TValue, bool> predicate) =>
enumerable.Where(kvp => predicate(kvp.Value));
}
public static class DictionaryExtensions
{
public static Option<TValue> Find<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key) =>
dictionary.TryGetValue(key, out var value)
? Option<TValue>.Some(value)
: Option<TValue>.None;
}