using System; using System.Collections; using System.Collections.Generic; using System.Threading.Tasks; using JetBrains.Annotations; namespace Kyoo.Utils { /// /// A set of extensions class for enumerable. /// public static class EnumerableExtensions { /// /// A Select where the index of the item can be used. /// /// The IEnumerable to map. If self is null, an empty list is returned /// The function that will map each items /// The type of items in /// The type of items in the returned list /// The list mapped. /// The list or the mapper can't be null [LinqTunnel] public static IEnumerable Map([NotNull] this IEnumerable self, [NotNull] Func mapper) { if (self == null) throw new ArgumentNullException(nameof(self)); if (mapper == null) throw new ArgumentNullException(nameof(mapper)); static IEnumerable Generator(IEnumerable self, Func mapper) { using IEnumerator enumerator = self.GetEnumerator(); int index = 0; while (enumerator.MoveNext()) { yield return mapper(enumerator.Current, index); index++; } } return Generator(self, mapper); } /// /// A map where the mapping function is asynchronous. /// Note: might interest you. /// /// The IEnumerable to map. /// The asynchronous function that will map each items /// The type of items in /// The type of items in the returned list /// The list mapped as an AsyncEnumerable /// The list or the mapper can't be null [LinqTunnel] public static IAsyncEnumerable MapAsync([NotNull] this IEnumerable self, [NotNull] Func> mapper) { if (self == null) throw new ArgumentNullException(nameof(self)); if (mapper == null) throw new ArgumentNullException(nameof(mapper)); static async IAsyncEnumerable Generator(IEnumerable self, Func> mapper) { using IEnumerator enumerator = self.GetEnumerator(); int index = 0; while (enumerator.MoveNext()) { yield return await mapper(enumerator.Current, index); index++; } } return Generator(self, mapper); } /// /// An asynchronous version of Select. /// /// The IEnumerable to map /// The asynchronous function that will map each items /// The type of items in /// The type of items in the returned list /// The list mapped as an AsyncEnumerable /// The list or the mapper can't be null [LinqTunnel] public static IAsyncEnumerable SelectAsync([NotNull] this IEnumerable self, [NotNull] Func> mapper) { if (self == null) throw new ArgumentNullException(nameof(self)); if (mapper == null) throw new ArgumentNullException(nameof(mapper)); static async IAsyncEnumerable Generator(IEnumerable self, Func> mapper) { using IEnumerator enumerator = self.GetEnumerator(); while (enumerator.MoveNext()) yield return await mapper(enumerator.Current); } return Generator(self, mapper); } /// /// Convert an AsyncEnumerable to a List by waiting for every item. /// /// The async list /// The type of items in the async list and in the returned list. /// A task that will return a simple list /// The list can't be null [LinqTunnel] public static Task> ToListAsync([NotNull] this IAsyncEnumerable self) { if (self == null) throw new ArgumentNullException(nameof(self)); static async Task> ToList(IAsyncEnumerable self) { List ret = new(); await foreach (T i in self) ret.Add(i); return ret; } return ToList(self); } /// /// If the enumerable is empty, execute an action. /// /// The enumerable to check /// The action to execute is the list is empty /// The type of items inside the list /// The iterable and the action can't be null. /// The iterator proxied, there is no dual iterations. [LinqTunnel] public static IEnumerable IfEmpty([NotNull] this IEnumerable self, [NotNull] Action action) { if (self == null) throw new ArgumentNullException(nameof(self)); if (action == null) throw new ArgumentNullException(nameof(action)); static IEnumerable Generator(IEnumerable self, Action action) { using IEnumerator enumerator = self.GetEnumerator(); if (!enumerator.MoveNext()) { action(); yield break; } do { yield return enumerator.Current; } while (enumerator.MoveNext()); } return Generator(self, action); } /// /// A foreach used as a function with a little specificity: the list can be null. /// /// The list to enumerate. If this is null, the function result in a no-op /// The action to execute for each arguments /// The type of items in the list public static void ForEach([CanBeNull] this IEnumerable self, Action action) { if (self == null) return; foreach (T i in self) action(i); } /// /// A foreach used as a function with a little specificity: the list can be null. /// /// The list to enumerate. If this is null, the function result in a no-op /// The action to execute for each arguments public static void ForEach([CanBeNull] this IEnumerable self, Action action) { if (self == null) return; foreach (object i in self) action(i); } /// /// A foreach used as a function with a little specificity: the list can be null. /// /// The list to enumerate. If this is null, the function result in a no-op /// The action to execute for each arguments public static async Task ForEachAsync([CanBeNull] this IEnumerable self, Func action) { if (self == null) return; foreach (object i in self) await action(i); } /// /// A foreach used as a function with a little specificity: the list can be null. /// /// The list to enumerate. If this is null, the function result in a no-op /// The asynchronous action to execute for each arguments /// The type of items in the list. public static async Task ForEachAsync([CanBeNull] this IEnumerable self, Func action) { if (self == null) return; foreach (T i in self) await action(i); } /// /// A foreach used as a function with a little specificity: the list can be null. /// /// The async list to enumerate. If this is null, the function result in a no-op /// The action to execute for each arguments /// The type of items in the list. public static async Task ForEachAsync([CanBeNull] this IAsyncEnumerable self, Action action) { if (self == null) return; await foreach (T i in self) action(i); } /// /// Split a list in a small chunk of data. /// /// The list to split /// The number of items in each chunk /// The type of data in the initial list. /// A list of chunks [LinqTunnel] public static IEnumerable> BatchBy(this List list, int countPerList) { for (int i = 0; i < list.Count; i += countPerList) yield return list.GetRange(i, Math.Min(list.Count - i, countPerList)); } /// /// Split a list in a small chunk of data. /// /// The list to split /// The number of items in each chunk /// The type of data in the initial list. /// A list of chunks [LinqTunnel] public static IEnumerable BatchBy(this IEnumerable list, int countPerList) { T[] ret = new T[countPerList]; int i = 0; using IEnumerator enumerator = list.GetEnumerator(); while (enumerator.MoveNext()) { ret[i] = enumerator.Current; i++; if (i < countPerList) continue; i = 0; yield return ret; } Array.Resize(ref ret, i); yield return ret; } } }