using System.Collections.Generic; using System; using System.Linq; using System.Collections.Immutable; namespace Barotrauma.Extensions { public static class IEnumerableExtensions { /// /// Randomizes the collection (using OrderBy) and returns it. /// public static T[] Randomize(this IList source, Rand.RandSync randSync = Rand.RandSync.Unsynced) { return source.OrderBy(i => Rand.Value(randSync)).ToArray(); } /// /// 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) => list.Shuffle(Rand.GetRNG(randSync)); public static void Shuffle(this IList list, Random rng) { int n = list.Count; while (n > 1) { n--; int k = rng.Next(n + 1); T value = list[k]; list[k] = list[n]; list[n] = value; } } public static T GetRandom(this IReadOnlyList source, Func predicate, Rand.RandSync randSync) { if (predicate == null) { return GetRandom(source, randSync); } return source.Where(predicate).ToArray().GetRandom(randSync); } /// /// Gets a random element of a list using one of the synced random number generators. /// It's recommended that you guarantee a deterministic order of the elements of the /// input list via sorting. /// /// List to pick a random element from /// Which RNG to use /// A random item from the list. Return value should match between clients and /// the server, if applicable. public static T GetRandom(this IReadOnlyList source, Rand.RandSync randSync) { int count = source.Count; return count == 0 ? default : source[Rand.Range(0, count, randSync)]; } public static T GetRandom(this IReadOnlyList source, Random random) { int count = source.Count; return count == 0 ? default : source[random.Next(0, count)]; } // The reason these "GetRandomUnsynced" methods exist is because // they can be used on all enumerables; GetRandom can only be used // on lists as they can be sorted to guarantee a certain order. public static T GetRandomUnsynced(this IEnumerable source, Func predicate) { if (predicate == null) { return GetRandomUnsynced(source); } return source.Where(predicate).GetRandomUnsynced(); } public static T GetRandomUnsynced(this IEnumerable source) { if (source is IReadOnlyList list) { return list.GetRandom(Rand.RandSync.Unsynced); } else { int count = source.Count(); return count == 0 ? default : source.ElementAt(Rand.Range(0, count, Rand.RandSync.Unsynced)); } } public static T GetRandom(this IEnumerable source, Random rand) where T : PrefabWithUintIdentifier { return source.OrderBy(p => p.UintIdentifier).ToArray().GetRandom(rand); } public static T GetRandom(this IEnumerable source, Rand.RandSync randSync) where T : PrefabWithUintIdentifier { return source.OrderBy(p => p.UintIdentifier).ToArray().GetRandom(randSync); } public static T GetRandom(this IEnumerable source, Func predicate, Rand.RandSync randSync) where T : PrefabWithUintIdentifier { return source.Where(predicate).OrderBy(p => p.UintIdentifier).ToArray().GetRandom(randSync); } public static T GetRandomByWeight(this IEnumerable source, Func weightSelector, Rand.RandSync randSync) { return ToolBox.SelectWeightedRandom(source, weightSelector, randSync); } /// /// 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); } } /// /// Iterates over all elements in a given enumerable and discards the result. /// public static void Consume(this IEnumerable enumerable) { foreach (var _ in enumerable) { /* do nothing */ } } /// /// 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); } } public static NetCollection ToNetCollection(this IEnumerable enumerable) => new NetCollection(enumerable.ToImmutableArray()); /// /// 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; } /// /// Equivalent to LINQ's Enumerable.Concat. The main difference is that this /// takes advantage of ICollection optimizations for Enumerable.Contains /// and Enumerable.Count. /// /// public static ICollection CollectionConcat(this IEnumerable self, IEnumerable other) => new CollectionConcat(self, other); public static IReadOnlyList ListConcat(this IEnumerable self, IEnumerable other) => new ListConcat(self, other); } }