using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using JetBrains.Annotations; namespace Robust.Shared.Utility { public static class Extensions { public static IList Clone(this IList listToClone) where T : ICloneable { return listToClone.Select(item => (T)item.Clone()).ToList(); } /// /// Creates a shallow clone of a list. /// Basically a new list with all the same elements. /// /// The list to shallow clone. /// The type of the list's elements. /// A new list with the same elements as . public static List ShallowClone(this List self) { var list = new List(self.Count); list.AddRange(self); return list; } public static Dictionary ShallowClone(this Dictionary self) where TKey : notnull { var dict = new Dictionary(self.Count); foreach (var item in self) { dict[item.Key] = item.Value; } return dict; } public static bool TryGetValue(this IList list, int index, out T value) { if (list.Count > index) { value = list[index]; return true; } value = default!; return false; } /// /// Remove an item from the list, replacing it with the one at the very end of the list. /// This means that the order will not be preserved, but it should be an O(1) operation. /// /// The index to remove /// The removed element public static T RemoveSwap(this IList list, int index) { // This method has no implementation details, // and changing the result of an operation is a breaking change. var old = list[index]; var replacement = list[list.Count - 1]; list[index] = replacement; // TODO: Any more efficient way to pop the last element off? list.RemoveAt(list.Count - 1); return old; } /// /// Pop an element from the end of a list, removing it from the list and returning it. /// /// The list to pop from. /// The type of the elements of the list. /// The popped off element. /// /// Thrown if the list is empty. /// public static T Pop(this IList list) { if (list.Count == 0) { throw new InvalidOperationException(); } var t = list[list.Count - 1]; list.RemoveAt(list.Count-1); return t; } /// /// Just like but returns null for value types as well. /// /// An to return an element from. /// A function to test each element for a condition. /// The type of the elements of . /// null if is empty or if no element passes the test specified by ; otherwise, the first element in that passes the test specified by . /// /// or is . public static TSource? FirstOrNull( this IEnumerable source, Func predicate) where TSource: struct { if (source == null) throw new ArgumentNullException(nameof (source)); if (predicate == null) throw new ArgumentNullException(nameof (predicate)); foreach (TSource source1 in source) { if (predicate(source1)) return source1; } return null; } /// /// Just like but returns null for value types as well. /// /// /// An to return the first element from. /// /// The type of the elements of . /// /// if is empty, otherwise, /// the first element in . /// /// /// is . public static TSource? FirstOrNull(this IEnumerable source) where TSource : struct { if (source == null) throw new ArgumentNullException(nameof (source)); using var enumerator = source.GetEnumerator(); if (!enumerator.MoveNext()) { return null; } return enumerator.Current; } /// /// Just like but returns null for value types as well. /// /// An to return an element from. /// A function to test each element for a condition. /// The type of the elements of . /// True if an element has been found. /// /// or is . public static bool TryFirstOrNull(this IEnumerable source, Func predicate, [NotNullWhen(true)] out TSource? element) where TSource : struct { element = source.FirstOrNull(predicate); return element != null; } /// /// Wraps Linq's FirstOrDefault. /// /// An to return an element from. /// A function to test each element for a condition. /// The type of the elements of . /// True if an element has been found. /// /// or is . public static bool TryFirstOrDefault(this IEnumerable source, Func predicate, [NotNullWhen(true)] out TSource? element) where TSource : class { element = source.FirstOrDefault(predicate); return element != null; } public static TValue GetOrNew(this IDictionary dict, TKey key) where TValue : new() where TKey : notnull { if (!dict.TryGetValue(key, out var value)) { value = new TValue(); dict.Add(key, value); } return value; } // More efficient than LINQ. public static KeyValuePair[] ToArray(this Dictionary dict) where TKey : notnull { var array = new KeyValuePair[dict.Count]; var i = 0; foreach (var kvPair in dict) { array[i] = kvPair; i += 1; } return array; } /// /// Tries to get a value from a dictionary and checks if that value is of type T /// /// The type that sould be casted to /// Whether the value was present in the dictionary and of the required type public static bool TryCastValue(this Dictionary dict, TKey key, [NotNullWhen(true)] out T? value) where TKey : notnull { if (dict.TryGetValue(key, out var untypedValue) && untypedValue is T typedValue) { value = typedValue; return true; } value = default; return false; } } }