using System; using System.Collections.Generic; using System.Linq; using Hazel; // ReSharper disable ConvertIfStatementToReturnStatement // ReSharper disable ForCanBeConvertedToForeach namespace EHR; public static class CollectionExtensions { /// /// Returns the key of a dictionary by its value /// /// The to search /// The used to search for the corresponding key /// The type of the keys in the /// The type of the values in the /// /// The key of the that corresponds to the given , or the /// default value of if the is not found in the /// /// public static TKey GetKeyByValue(this Dictionary dictionary, TValue value) { foreach (KeyValuePair pair in dictionary) { if (pair.Value.Equals(value)) return pair.Key; } return default(TKey); } /// /// Sets the value for all existing keys in a dictionary to a specific value /// /// /// /// /// public static void SetAllValues(this Dictionary dictionary, TValue value) { foreach (TKey key in dictionary.Keys.ToArray()) dictionary[key] = value; } /// /// Adjusts the value for all existing keys in a dictionary /// /// The dictionary to adjust the values of /// The function to adjust the values with /// /// public static void AdjustAllValues(this Dictionary dictionary, Func adjust) { foreach (TKey key in dictionary.Keys.ToArray()) dictionary[key] = adjust(dictionary[key]); } /// /// Returns a random element from a collection /// /// The collection /// The type of the collection /// /// A random element from the collection, or the default value of if the collection is /// empty /// public static T RandomElement(this IList collection) { if (collection.Count == 0) return default(T); return collection[IRandom.Instance.Next(collection.Count)]; } /// /// Returns a random element from a collection /// /// The collection /// The type of the collection /// /// A random element from the collection, or the default value of if the collection is /// empty /// public static T RandomElement(this IEnumerable collection) { if (collection is IList list) return list.RandomElement(); return collection.ToList().RandomElement(); } /// /// Combines multiple collections into a single collection /// /// The collection to start with /// The other collections to add to /// The type of the elements in the collections to combine /// /// A collection containing all elements of and all /// /// public static IEnumerable CombineWith(this IEnumerable firstCollection, params IEnumerable[] collections) { return firstCollection.Concat(collections.Flatten()); } /// /// Executes an action for each element in a collection /// /// The collection to iterate over /// The action to execute for each element /// The type of the elements in the collection public static void Do(this IEnumerable collection, Action action) { if (collection is List list) { for (var i = 0; i < list.Count; i++) action(list[i]); return; } foreach (T element in collection) action(element); } /// /// Executes an action for each element in a collection if the predicate is true /// /// The collection to iterate over /// Whether to use a fast loop or linq /// The predicate to check for each element /// The action to execute for each element that satisfies the predicate /// The type of the elements in the collection public static void DoIf(this IEnumerable collection, Func predicate, Action action, bool fast = true) { if (fast) { if (collection is List list) { for (var i = 0; i < list.Count; i++) { T element = list[i]; if (predicate(element)) action(element); } return; } foreach (T element in collection) { if (predicate(element)) action(element); } return; } collection.Where(predicate).ToArray().Do(action); } /// /// Splits a collection into two collections based on a predicate /// /// The collection to split /// The predicate to split the collection by /// The type of the elements in the collection /// /// A tuple containing two collections: one with elements that satisfy the predicate, and one with elements that /// do not /// public static (List TrueList, List FalseList) Split(this IEnumerable collection, Func predicate) { var list1 = new List(); var list2 = new List(); foreach (T element in collection) { if (predicate(element)) list1.Add(element); else list2.Add(element); } return (list1, list2); } /// /// Adds a range of elements to a dictionary /// /// The dictionary to add elements to /// The dictionary containing the elements to add /// /// Whether to override existing keys in the with the /// same keys in the dictionary. If true, the same keys in the /// will be overwritten with the values from the dictionary. /// If false, the same keys in the will be kept and the values from the /// dictionary will be ignored /// /// The type of the keys in the dictionaries /// The type of the values in the dictionaries /// The with the elements from the dictionary added public static Dictionary AddRange(this Dictionary dictionary, Dictionary other, bool overrideExistingKeys = true) { foreach ((TKey key, TValue value) in other) { if (overrideExistingKeys || !dictionary.ContainsKey(key)) dictionary[key] = value; } return dictionary; } /// /// Flattens a collection of collections into a single collection /// /// The collection of collections to flatten /// The type of the elements in the collections /// A single collection containing all elements of the collections in public static IEnumerable Flatten(this IEnumerable> collection) { return collection.SelectMany(x => x); } /// /// Determines whether a collection contains any elements that satisfy a predicate and returns the first element that /// satisfies the predicate /// /// The collection to search /// The predicate to check for each element /// /// The first element that satisfies the predicate, or the default value of /// if no elements satisfy the predicate /// /// The type of the elements in the collection /// true if the collection contains any elements that satisfy the predicate, false otherwise public static bool FindFirst(this IEnumerable collection, Func predicate, out T element) { if (collection is List list) { for (var i = 0; i < list.Count; i++) { T item = list[i]; if (predicate(item)) { element = item; return true; } } element = default(T); return false; } foreach (T item in collection) { if (predicate(item)) { element = item; return true; } } element = default(T); return false; } public static void NotifyPlayers(this IEnumerable players, string text, float time = 6f, bool overrideAll = false, bool log = true, bool setName = true) { var sender = CustomRpcSender.Create("NotifyPlayers", SendOption.Reliable); var hasValue = false; foreach (PlayerControl player in players) { sender.Notify(player, text, time, overrideAll, log, setName); hasValue = true; if (sender.stream.Length > 800) { sender.SendMessage(); sender = CustomRpcSender.Create("NotifyPlayers", SendOption.Reliable); hasValue = false; } } sender.SendMessage(dispose: !hasValue); } #region ToValidPlayers /// /// Converts a collection of player IDs to a collection of instances /// /// /// public static IEnumerable ToValidPlayers(this IEnumerable playerIds) { return playerIds.Select(Utils.GetPlayer).Where(x => x != null); } /// /// Converts a list of player IDs to a list of instances /// /// /// public static List ToValidPlayers(this List playerIds) { return playerIds.ConvertAll(Utils.GetPlayer).FindAll(x => x != null); } #endregion #region Without /// /// Removes an element from a collection /// /// The collection to remove the element from /// The element to remove /// The type of the elements in the collection /// /// A collection containing all elements of except for /// public static IEnumerable Without(this IEnumerable collection, T element) { return collection.Where(x => !x.Equals(element)); } /// /// Removes an element from a collection /// /// The collection to remove the element from /// The element to remove /// /// A collection containing all elements of except for /// public static IEnumerable Without(this IEnumerable collection, PlayerControl element) { return collection.Where(x => x.PlayerId != element.PlayerId); } /// /// Removes an element from a collection /// /// The collection to remove the element from /// The element to remove /// /// A collection containing all elements of except for /// public static IEnumerable Without(this IEnumerable collection, PlainShipRoom element) { return collection.Where(x => x != element); } #endregion #region Shuffle /// /// Shuffles all elements in a collection randomly /// /// The type of the collection /// The collection to be shuffled /// A new, shuffled collection as a public static List Shuffle(this IEnumerable collection) { List list = collection.ToList(); int n = list.Count; var r = IRandom.Instance; while (n > 1) { n--; int k = r.Next(n + 1); (list[n], list[k]) = (list[k], list[n]); } return list; } /// /// Shuffles all elements in a collection randomly /// /// The collection to be shuffled /// The type of the collection /// The same collection with its elements shuffled public static List Shuffle(this List collection) { int n = collection.Count; var r = IRandom.Instance; while (n > 1) { n--; int k = r.Next(n + 1); (collection[n], collection[k]) = (collection[k], collection[n]); } return collection; } /// /// Shuffles all elements in an array randomly /// /// The array to be shuffled /// The type of the array /// The same array with its elements shuffled public static T[] Shuffle(this T[] collection) { int n = collection.Length; var r = IRandom.Instance; while (n > 1) { n--; int k = r.Next(n + 1); (collection[n], collection[k]) = (collection[k], collection[n]); } return collection; } #endregion #region Partition /// /// Partitions a collection into a specified number of parts /// /// The collection to partition /// The number of parts to partition the collection into /// The type of the elements in the collection /// A collection of collections, each containing a part of the original collection public static IEnumerable> Partition(this IEnumerable collection, int parts) { List list = collection.ToList(); int length = list.Count; if (parts <= 0 || length == 0) yield break; if (parts > length) parts = length; int size = length / parts; int remainder = length % parts; var index = 0; for (var i = 0; i < parts; i++) { int partSize = size + (i < remainder ? 1 : 0); yield return list.Skip(index).Take(partSize); index += partSize; } } /// /// Partitions a list into a specified number of parts /// /// The list to partition /// The number of parts to partition the list into /// The type of the elements in the list /// A list of lists, each containing a part of the original list public static IEnumerable> Partition(this IList collection, int parts) { int length = collection.Count; if (parts <= 0 || length == 0) yield break; if (parts > length) parts = length; int size = length / parts; int remainder = length % parts; var index = 0; for (var i = 0; i < parts; i++) { int partSize = size + (i < remainder ? 1 : 0); yield return collection.Skip(index).Take(partSize); index += partSize; } } #endregion } public static class Loop { public static void Times(int count, Action action) { for (var i = 0; i < count; i++) action(i); } }