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);
}
}