526 lines
13 KiB
C#
526 lines
13 KiB
C#
using System.Diagnostics.CodeAnalysis;
|
|
|
|
namespace InnovEnergy.Lib.Utils;
|
|
|
|
public static class EnumerableUtils
|
|
{
|
|
public static T? FirstOrNullableDefault<T>(this IEnumerable<T> ts, Func<T, Boolean> predicate) where T : struct
|
|
{
|
|
return ts
|
|
.Where(predicate)
|
|
.OfType<T?>()
|
|
.FirstOrDefault();
|
|
}
|
|
|
|
public static Int32 SequenceHash<T>(this IEnumerable<T> ts)
|
|
{
|
|
var hash = new HashCode();
|
|
hash.Add(typeof(T).GetHashCode());
|
|
foreach (var t in ts)
|
|
hash.Add(t?.GetHashCode());
|
|
|
|
return hash.ToHashCode();
|
|
}
|
|
|
|
public static IList<T> Push<T>(this IList<T> l, T t)
|
|
{
|
|
l.Add(t);
|
|
return l;
|
|
}
|
|
|
|
public static T Pop<T>(this IList<T> l)
|
|
{
|
|
var i = l.Count - 1;
|
|
var t = l[i];
|
|
l.RemoveAt(i);
|
|
return t;
|
|
}
|
|
|
|
// public static async Task<IEnumerable<R>> SelectManyAsync<T, R>(this IEnumerable<T> ts, Func<T, Task<IEnumerable<R>>> func)
|
|
// {
|
|
// var whenAll = await Task.WhenAll(ts.Select(func));
|
|
// return whenAll.SelectMany(s => s);
|
|
// }
|
|
|
|
|
|
// public static async Task<IEnumerable<R>> SelectManyAsync<T, R>(this IEnumerable<T> ts,
|
|
// Func<T, Task<IEnumerable<R>>> func)
|
|
// {
|
|
// var result = Enumerable.Empty<R>();
|
|
//
|
|
// foreach (var t in ts)
|
|
// {
|
|
// result = result.Concat(await func(t));
|
|
// }
|
|
//
|
|
// return result;
|
|
// }
|
|
|
|
|
|
// public static IEnumerable<Task<T>> SelectAsync<T>(this Task<IEnumerable<T>> ts)
|
|
// {
|
|
// yield return ts.ContinueWith(r => r.Result.First());
|
|
//
|
|
// foreach (var t in ts.Result.Skip(1))
|
|
// yield return Task.FromResult(t);
|
|
// }
|
|
|
|
|
|
public static Queue<T> ToQueue<T>(this IEnumerable<T> ts)
|
|
{
|
|
var q = new Queue<T>();
|
|
foreach (var t in ts)
|
|
q.Enqueue(t);
|
|
|
|
return q;
|
|
}
|
|
|
|
public static Stack<T> ToStack<T>(this IEnumerable<T> ts)
|
|
{
|
|
var s = new Stack<T>();
|
|
foreach (var t in ts)
|
|
s.Push(t);
|
|
|
|
return s;
|
|
}
|
|
|
|
public static async IAsyncEnumerable<R>
|
|
SelectManyAsync<T, R>(this IEnumerable<T> ts, Func<T, Task<IEnumerable<R>>> func)
|
|
{
|
|
foreach (var t in ts)
|
|
{
|
|
var rs = await func(t);
|
|
foreach (var r in rs)
|
|
yield return r;
|
|
}
|
|
}
|
|
|
|
|
|
public static IEnumerable<T> SelectTuple<L, R, T>(this IEnumerable<(L l, R r)> tuples, Func<L, R, T> map)
|
|
{
|
|
return tuples.Select(tuple => map(tuple.l, tuple.r));
|
|
}
|
|
|
|
|
|
public static Boolean AllEqual<T, R>(this IEnumerable<T> ts, Func<T, R> map)
|
|
{
|
|
return ts.Select(map).AllEqual();
|
|
}
|
|
|
|
public static Boolean AllEqual<T>(this IEnumerable<T> ts)
|
|
{
|
|
return !ts.Distinct().Skip(1).Any();
|
|
}
|
|
|
|
public static IReadOnlyList<T> NullableToReadOnlyList<T>(this T? t)
|
|
{
|
|
return t is null ? Array.Empty<T>() : new[] { t };
|
|
}
|
|
|
|
public static IEnumerable<T> NullableToEnumerable<T>(this T? t)
|
|
{
|
|
if (t is not null)
|
|
yield return t;
|
|
}
|
|
|
|
public static IEnumerable<(T left, T right)> Pairwise<T>(this IEnumerable<T> ts)
|
|
{
|
|
using var e = ts.GetEnumerator();
|
|
|
|
if (!e.MoveNext())
|
|
yield break;
|
|
|
|
var left = e.Current;
|
|
|
|
while (e.MoveNext())
|
|
{
|
|
yield return (left, e.Current);
|
|
left = e.Current;
|
|
}
|
|
}
|
|
|
|
|
|
public static IEnumerable<(T left, T right)> Pairwise<T>(this IEnumerable<T> ts, T seed)
|
|
{
|
|
using var e = ts.GetEnumerator();
|
|
|
|
var left = seed;
|
|
|
|
while (e.MoveNext())
|
|
{
|
|
yield return (left, e.Current);
|
|
left = e.Current;
|
|
}
|
|
}
|
|
|
|
|
|
public static IEnumerable<Int32> To(this Int32 start, Int32 end)
|
|
{
|
|
var d = Math.Sign(end - start);
|
|
|
|
for (var i = start; i != end; i += d)
|
|
yield return i;
|
|
}
|
|
|
|
public static IEnumerable<UInt32> To(this UInt32 start, UInt32 end)
|
|
{
|
|
var d = Math.Sign(end - start).ConvertTo<UInt32>();
|
|
|
|
for (var i = start; i != end; i += d)
|
|
yield return i;
|
|
}
|
|
|
|
public static IReadOnlyList<T> ToReadOnlyList<T>(this IEnumerable<T> src)
|
|
{
|
|
return src switch
|
|
{
|
|
T[] a => a,
|
|
List<T> l => l,
|
|
_ => src.ToList()
|
|
};
|
|
}
|
|
|
|
public static IReadOnlyList<T> ToReadOnlyList<T>(this IEnumerable<T> src, Int32 size) => ToArray(src, size);
|
|
|
|
public static List<T> ToList<T>(this IEnumerable<T> src, Int32 initialSize)
|
|
{
|
|
var list = new List<T>(initialSize);
|
|
list.AddRange(src);
|
|
return list;
|
|
}
|
|
|
|
|
|
public static IEnumerable<T> PadEnd<T>(this IEnumerable<T> src, Int32 length, T padding)
|
|
{
|
|
using var enumerator = src.GetEnumerator();
|
|
|
|
while (enumerator.MoveNext() && length-- > 0)
|
|
yield return enumerator.Current;
|
|
|
|
while (length-- > 0)
|
|
yield return padding;
|
|
}
|
|
|
|
public static IEnumerable<T> PadStart<T>(this IEnumerable<T> src, Int32 length, T padding)
|
|
{
|
|
return src
|
|
.Reverse()
|
|
.PadEnd(length, padding)
|
|
.Reverse();
|
|
}
|
|
|
|
|
|
public static IEnumerator<T> AsSingleEnumerator<T>(this T t)
|
|
{
|
|
yield return t;
|
|
}
|
|
|
|
public static IEnumerable<T> AsSingleEnumerable<T>(this T t)
|
|
{
|
|
yield return t;
|
|
}
|
|
|
|
public static IEnumerable<T> Flatten<T>(this IEnumerable<IEnumerable<T>> src) => src.SelectMany(s => s);
|
|
|
|
[SuppressMessage("ReSharper", "PossibleMultipleEnumeration")]
|
|
public static IEnumerable<T> ForEach<T>(this IEnumerable<T> enumerable, Action<T> action)
|
|
{
|
|
foreach (var e in enumerable)
|
|
action(e);
|
|
|
|
return enumerable;
|
|
}
|
|
|
|
public static IEnumerable<T> Do<T>(this IEnumerable<T> enumerable, Action<T> action)
|
|
{
|
|
return enumerable.Select(e =>
|
|
{
|
|
action(e);
|
|
return e;
|
|
});
|
|
}
|
|
|
|
public static void ForEach<T, R>(this IEnumerable<T> enumerable, Func<T, R> func)
|
|
{
|
|
foreach (var e in enumerable)
|
|
func(e);
|
|
}
|
|
|
|
public static IEnumerable<T> EmptyIfNull<T>(this IEnumerable<T>? ts)
|
|
{
|
|
return ts ?? Enumerable.Empty<T>();
|
|
}
|
|
|
|
public static T ElementAtOr<T>(this IEnumerable<T> ts, Int32 index, T defaultValue)
|
|
{
|
|
return ts.ElementAtOrDefault(index) ?? defaultValue;
|
|
}
|
|
|
|
public static IEnumerable<T> Unfold<T>(this T? seed, Func<T, T?> next)
|
|
{
|
|
var value = seed;
|
|
while (value is not null)
|
|
{
|
|
yield return value;
|
|
value = next(value);
|
|
}
|
|
}
|
|
|
|
|
|
public static IEnumerable<T> InfinitelyMany<T>(this T value)
|
|
{
|
|
while (true)
|
|
yield return value;
|
|
|
|
// ReSharper disable once IteratorNeverReturns
|
|
}
|
|
|
|
public static T[] ToArray<T>(this IEnumerable<T> ts, Int32 n)
|
|
{
|
|
if (ts is T[] ta)
|
|
return ta;
|
|
|
|
var array = new T[n];
|
|
var i = 0;
|
|
|
|
using var enumerator = ts.GetEnumerator();
|
|
|
|
while (i < n && enumerator.MoveNext())
|
|
array[i++] = enumerator.Current;
|
|
|
|
if (enumerator.MoveNext())
|
|
throw new ArgumentOutOfRangeException(nameof(ts));
|
|
|
|
return array;
|
|
}
|
|
|
|
public static T[] ToArray<T>(this IReadOnlyList<T> ts)
|
|
{
|
|
return ts as T[] ?? ts.ToArray(ts.Count);
|
|
}
|
|
|
|
|
|
|
|
public static IEnumerable<T> Concat<T>(this IEnumerable<T> ts, T last)
|
|
{
|
|
foreach (var t in ts)
|
|
yield return t;
|
|
|
|
yield return last;
|
|
}
|
|
|
|
public static IEnumerable<T> Many<T>(this T value)
|
|
{
|
|
yield return value;
|
|
}
|
|
|
|
public static IEnumerable<T> PadWith<T>(this IEnumerable<T> enumerable, T padding)
|
|
{
|
|
return enumerable.Concat(padding.InfinitelyMany());
|
|
}
|
|
|
|
public static IEnumerable<T> PadWith<T>(this IEnumerable<T> enumerable, T padding, Int32 maxLength)
|
|
{
|
|
return enumerable
|
|
.PadWith(padding)
|
|
.Take(maxLength);
|
|
}
|
|
|
|
|
|
public static IEnumerable<T> Unless<T>(this IEnumerable<T> seq, Func<T, Boolean> filter)
|
|
{
|
|
return seq.Where(e => !filter(e));
|
|
}
|
|
|
|
public static IEnumerable<T> Intersperse<T>(this IEnumerable<T> enumerable, T interspersed)
|
|
{
|
|
using var e = enumerable.GetEnumerator();
|
|
|
|
if (!e.MoveNext()) yield break;
|
|
|
|
yield return e.Current;
|
|
|
|
while (e.MoveNext())
|
|
{
|
|
yield return interspersed;
|
|
yield return e.Current;
|
|
}
|
|
}
|
|
|
|
public static T Next<T>(this IEnumerator<T> enumerator)
|
|
{
|
|
enumerator.MoveNext();
|
|
return enumerator.Current;
|
|
}
|
|
|
|
public static IEnumerable<T> Scan<T>(this IEnumerable<T> src, Func<T, T, T> next)
|
|
{
|
|
using var e = src.GetEnumerator();
|
|
|
|
if (!e.MoveNext())
|
|
yield break;
|
|
|
|
var state = e.Current;
|
|
yield return state;
|
|
|
|
while(e.MoveNext())
|
|
{
|
|
state = next(state, e.Current);
|
|
yield return state;
|
|
}
|
|
|
|
}
|
|
|
|
public static IEnumerable<R> Scan<T, R>(this IEnumerable<T> src, R seed, Func<R, T, R> next)
|
|
{
|
|
var current = seed;
|
|
|
|
foreach (var t in src)
|
|
{
|
|
yield return current;
|
|
current = next(current, t);
|
|
}
|
|
}
|
|
|
|
|
|
[SuppressMessage("ReSharper", "AccessToDisposedClosure")]
|
|
public static IEnumerable<IEnumerable<T>> GroupUntil<T>(this IEnumerable<T> sequence, Func<T, T, Boolean> splitBetween)
|
|
{
|
|
using var e = sequence.GetEnumerator();
|
|
|
|
if (!e.MoveNext())
|
|
yield break; // empty sequence
|
|
|
|
var moreInSeq = false;
|
|
var moreInGroup = false;
|
|
|
|
do
|
|
{
|
|
yield return GetGroup();
|
|
while (moreInGroup)
|
|
MoveNext();
|
|
}
|
|
while (moreInSeq);
|
|
|
|
void MoveNext()
|
|
{
|
|
var prev = e.Current;
|
|
moreInSeq = e.MoveNext();
|
|
moreInGroup = moreInSeq && !splitBetween(prev, e.Current);
|
|
}
|
|
|
|
IEnumerable<T> GetGroup()
|
|
{
|
|
do
|
|
{
|
|
yield return e.Current;
|
|
MoveNext();
|
|
}
|
|
while (moreInGroup);
|
|
}
|
|
}
|
|
|
|
public static IEnumerable<T> NotNull<T>(this IEnumerable<T?> ts)
|
|
{
|
|
return ts.Where(t => t != null)!;
|
|
}
|
|
|
|
|
|
public static IEnumerable<Double> RandomWalk()
|
|
{
|
|
var rng = new Random();
|
|
var x = NextDx();
|
|
|
|
while (true)
|
|
{
|
|
yield return x;
|
|
|
|
x = (x + NextDx()) / 2;
|
|
}
|
|
|
|
Double NextDx() => rng.NextDouble() * 2 - 1;
|
|
}
|
|
|
|
|
|
public static IEnumerable<Double> Integrate(this IEnumerable<Double> source, Double initConst = 0)
|
|
{
|
|
return source.Scan(initConst, (x, y) => x + y);
|
|
}
|
|
|
|
public static IEnumerable<Double> IntegrateNormalize(this IEnumerable<Double> source, Double initConst = 0)
|
|
{
|
|
return source.Scan(initConst, (x, y) => Math.Tanh(x + y));
|
|
}
|
|
|
|
|
|
public static IEnumerable<Double> MovingAverage(this IEnumerable<Double> source, Int32 windowSize)
|
|
{
|
|
if (windowSize < 1)
|
|
throw new ArgumentException(nameof(windowSize) + " must be positive", nameof(windowSize));
|
|
|
|
using var e = source.GetEnumerator();
|
|
|
|
if (!e.MoveNext()) yield break;
|
|
|
|
var state = e.Current;
|
|
|
|
do
|
|
{
|
|
state = ((windowSize - 1) * state + e.Current) / windowSize;
|
|
yield return state;
|
|
}
|
|
while (e.MoveNext());
|
|
}
|
|
|
|
|
|
public static IEnumerable<T> ZeroOrOne<T>(this T? t)
|
|
{
|
|
if (t is not null)
|
|
yield return t;
|
|
}
|
|
|
|
|
|
public static IEnumerable<IEnumerable<T>> Transpose<T>(this IEnumerable<IEnumerable<T>> src)
|
|
{
|
|
return src
|
|
.SelectMany(line => line.Select((element, column) => (element, column)))
|
|
.GroupBy(i => i.column, i => i.element);
|
|
}
|
|
|
|
public static T GetNext<T>(this IReadOnlyList<T> ts, T current)
|
|
{
|
|
return ts
|
|
.Concat(ts)
|
|
.SkipWhile(t => !t!.Equals(current))
|
|
.Skip(1)
|
|
.First();
|
|
}
|
|
|
|
public static T GetPrevious<T>(this IReadOnlyList<T> ts, T current)
|
|
{
|
|
return GetNext(ts.Reverse().ToArray(ts.Count), current);
|
|
}
|
|
|
|
|
|
public static IEnumerable<R> Memoize<T, R>(this IEnumerable<T> src, Func<T, R> map)
|
|
{
|
|
var cache = new List<R>();
|
|
|
|
return src
|
|
.Select((e, i) => i < cache.Count
|
|
? cache[i]
|
|
: map(e).Apply(cache.Add));
|
|
}
|
|
|
|
|
|
public static HashSet<T> ToHashSet<T>(this IEnumerable<T> source, IEqualityComparer<T>? comparer = null)
|
|
{
|
|
return new HashSet<T>(source, comparer);
|
|
}
|
|
|
|
public static IEnumerable<Int32> Times(this Int32 n)
|
|
{
|
|
return Enumerable.Range(0, n);
|
|
}
|
|
|
|
} |