using System.Collections.Generic; using System; using System.Linq; namespace Barotrauma.Extensions { public static class IEnumerableExtensions { /// /// Randomizes the collection (using OrderBy) and returns it. /// public static IOrderedEnumerable Randomize(this IEnumerable source, Rand.RandSync randSync = Rand.RandSync.Unsynced) { return source.OrderBy(i => Rand.Value(randSync)); } /// /// Randomizes the list in place without creating a new collection, using a Fisher-Yates-based algorithm. /// public static void Shuffle(this IList list, Rand.RandSync randSync = Rand.RandSync.Unsynced) { int n = list.Count; while (n > 1) { n--; int k = Rand.Int(n + 1, randSync); T value = list[k]; list[k] = list[n]; list[n] = value; } } public static T GetRandom(this IEnumerable source, Func predicate, Rand.RandSync randSync = Rand.RandSync.Unsynced) { if (predicate == null) { return GetRandom(source, randSync); } return source.Where(predicate).GetRandom(randSync); } public static T GetRandom(this IEnumerable source, Rand.RandSync randSync = Rand.RandSync.Unsynced) { if (source is IList list) { int count = list.Count; return count == 0 ? default : list[Rand.Range(0, count, randSync)]; } else { int count = source.Count(); return count == 0 ? default : source.ElementAt(Rand.Range(0, count, randSync)); } } public static T GetRandom(this IEnumerable source, Random random) { if (source is IList list) { int count = list.Count; return count == 0 ? default : list[random.Next(0, count)]; } else { int count = source.Count(); return count == 0 ? default : source.ElementAt(random.Next(0, count)); } } public static T RandomElementByWeight(this IEnumerable source, Func weightSelector, Rand.RandSync randSync = Rand.RandSync.Unsynced) { float totalWeight = source.Sum(weightSelector); float itemWeightIndex = Rand.Range(0f, 1f, randSync) * totalWeight; float currentWeightIndex = 0; foreach (T weightedItem in source) { float weight = weightSelector(weightedItem); currentWeightIndex += weight; if (currentWeightIndex >= itemWeightIndex) { return weightedItem; } } return default; } /// /// Executes an action that modifies the collection on each element (such as removing items from the list). /// Creates a temporary list, unless the collection is empty. /// public static void ForEachMod(this IEnumerable source, Action action) { if (source.None()) { return; } var temp = new List(source); temp.ForEach(action); } /// /// Generic version of List.ForEach. /// Performs the specified action on each element of the collection (short hand for a foreach loop). /// public static void ForEach(this IEnumerable source, Action action) { foreach (var item in source) { action(item); } } /// /// Shorthand for !source.Any(predicate) -> i.e. not any. /// public static bool None(this IEnumerable source, Func predicate = null) { if (predicate == null) { return !source.Any(); } else { return !source.Any(predicate); } } public static bool Multiple(this IEnumerable source, Func predicate = null) { if (predicate == null) { return source.Count() > 1; } else { return source.Count(predicate) > 1; } } public static IEnumerable ToEnumerable(this T item) { yield return item; } // source: https://stackoverflow.com/questions/19237868/get-all-children-to-one-list-recursive-c-sharp public static IEnumerable SelectManyRecursive(this IEnumerable source, Func> selector) { var result = source.SelectMany(selector); if (!result.Any()) { return result; } return result.Concat(result.SelectManyRecursive(selector)); } public static void AddIfNotNull(this IList source, T value) { if (value != null) { source.Add(value); } } /// /// Returns whether a given collection has at least a certain amount /// of elements for which the predicate returns true. /// /// Input collection /// How many elements to match before stopping /// Predicate used to evaluate the elements public static bool AtLeast(this IEnumerable source, int amount, Predicate predicate) { foreach (T elem in source) { if (predicate(elem)) { amount--; } if (amount <= 0) { return true; } } return false; } /// /// Returns the maximum element in a given enumerable, or null if there /// aren't any elements in the input. /// /// Input collection /// Maximum element or null public static T? MaxOrNull(this IEnumerable enumerable) where T : struct, IComparable { T? retVal = null; foreach (T v in enumerable) { if (!retVal.HasValue || v.CompareTo(retVal.Value) > 0) { retVal = v; } } return retVal; } } }