using System; using System.Collections; using System.Collections.Generic; using System.Linq; using UnityEngine; namespace Unity.VisualScripting { public static class LinqUtility { public static IEnumerable Concat(params IEnumerable[] enumerables) { foreach (var enumerable in enumerables.NotNull()) { foreach (var item in enumerable.OfType()) { yield return item; } } } public static IEnumerable DistinctBy(this IEnumerable items, Func property) { return items.GroupBy(property).Select(x => x.First()); } public static IEnumerable NotNull(this IEnumerable enumerable) { return enumerable.Where(i => i != null); } public static IEnumerable Yield(this T t) { yield return t; } public static HashSet ToHashSet(this IEnumerable enumerable) { return new HashSet(enumerable); } public static void AddRange(this ICollection collection, IEnumerable items) { foreach (var item in items) { collection.Add(item); } } public static void AddRange(this IList list, IEnumerable items) { foreach (var item in items) { list.Add(item); } } // NETUP: Replace with IReadOnlyCollection, IReadOnlyList public static ICollection AsReadOnlyCollection(this IEnumerable enumerable) { if (enumerable is ICollection) { return (ICollection)enumerable; } else { return enumerable.ToList().AsReadOnly(); } } public static IList AsReadOnlyList(this IEnumerable enumerable) { if (enumerable is IList) { return (IList)enumerable; } else { return enumerable.ToList().AsReadOnly(); } } public static IEnumerable Flatten ( this IEnumerable source, Func> childrenSelector ) { var flattenedList = source; foreach (var element in source) { flattenedList = flattenedList.Concat(childrenSelector(element).Flatten(childrenSelector)); } return flattenedList; } public static IEnumerable IntersectAll(this IEnumerable> groups) { HashSet hashSet = null; foreach (var group in groups) { if (hashSet == null) { hashSet = new HashSet(group); } else { hashSet.IntersectWith(group); } } return hashSet == null ? Enumerable.Empty() : hashSet.AsEnumerable(); } public static IEnumerable OrderByDependencies(this IEnumerable source, Func> getDependencies, bool throwOnCycle = true) { var sorted = new List(); var visited = HashSetPool.New(); foreach (var item in source) { OrderByDependenciesVisit(item, visited, sorted, getDependencies, throwOnCycle); } HashSetPool.Free(visited); return sorted; } private static void OrderByDependenciesVisit(T item, HashSet visited, List sorted, Func> getDependencies, bool throwOnCycle) { if (!visited.Contains(item)) { visited.Add(item); foreach (var dependency in getDependencies(item)) { OrderByDependenciesVisit(dependency, visited, sorted, getDependencies, throwOnCycle); } sorted.Add(item); } else { if (throwOnCycle && !sorted.Contains(item)) { throw new InvalidOperationException("Cyclic dependency."); } } } public static IEnumerable OrderByDependers(this IEnumerable source, Func> getDependers, bool throwOnCycle = true) { // TODO: Optimize, or use another algorithm (Kahn's?) // Convert dependers to dependencies var dependencies = new Dictionary>(); foreach (var dependency in source) { foreach (var depender in getDependers(dependency)) { if (!dependencies.ContainsKey(depender)) { dependencies.Add(depender, new HashSet()); } dependencies[depender].Add(dependency); } } return source.OrderByDependencies(depender => { if (dependencies.ContainsKey(depender)) { return dependencies[depender]; } else { return Enumerable.Empty(); } }, throwOnCycle); } public static IEnumerable Catch(this IEnumerable source, Action @catch) { Ensure.That(nameof(source)).IsNotNull(source); using (var enumerator = source.GetEnumerator()) { bool success; do { try { success = enumerator.MoveNext(); } catch (OperationCanceledException) { yield break; } catch (Exception ex) { @catch?.Invoke(ex); success = false; } if (success) { yield return enumerator.Current; } } while (success); } } public static IEnumerable Catch(this IEnumerable source, ICollection exceptions) { Ensure.That(nameof(exceptions)).IsNotNull(exceptions); return source.Catch(exceptions.Add); } public static IEnumerable CatchAsLogError(this IEnumerable source, string message) { return source.Catch((ex) => Debug.LogError(message + "\n" + ex.ToString())); } public static IEnumerable CatchAsLogWarning(this IEnumerable source, string message) { return source.Catch((ex) => Debug.LogWarning(message + "\n" + ex.ToString())); } } }