/****************************************************************************** * Copyright (C) Ultraleap, Inc. 2011-2021. * * * * Use subject to the terms of the Apache License 2.0 available at * * http://www.apache.org/licenses/LICENSE-2.0, or another agreement * * between Ultraleap and you, your company or other organization. * ******************************************************************************/ using System; using System.Collections.Generic; namespace Leap.Unity.Query { public static class QueryCollapseExtensions { /// /// Returns true if all elements in the Query satisfy the predicate. /// public static bool All(this Query query, Func predicate) { using (var slice = query.Deconstruct()) { for (int i = 0; i < slice.Count; i++) { if (!predicate(slice[i])) { return false; } } return true; } } /// /// Returns true if all elements in the Query are equal to the same value. /// Will always return true for Queries with one or zero elements. /// public static bool AllEqual(this Query query) { using (var slice = query.Deconstruct()) { if (slice.Count <= 1) { return true; } var comparer = EqualityComparer.Default; var first = slice[0]; for (int i = 1; i < slice.Count; i++) { if (!comparer.Equals(first, slice[i])) { return false; } } return true; } } /// /// Returns true if the Query has any elements in it. /// public static bool Any(this Query query) { return query.Count() > 0; } /// /// Returns true if any elements in the Query satisfy the predicate. /// public static bool Any(this Query query, Func predicate) { using (var slice = query.Deconstruct()) { for (int i = 0; i < slice.Count; i++) { if (predicate(slice[i])) { return true; } } return false; } } /// /// Returns the average of a Query of floats. /// public static float Average(this Query query) { using (var slice = query.Deconstruct()) { float sum = 0; for (int i = 0; i < slice.Count; i++) { sum += slice[i]; } return sum / slice.Count; } } /// /// Returns the average of a Query of doubles. /// public static double Average(this Query query) { using (var slice = query.Deconstruct()) { double sum = 0; for (int i = 0; i < slice.Count; i++) { sum += slice[i]; } return sum / slice.Count; } } /// /// Returns true if any element in the Query is equal to a specific value. /// public static bool Contains(this Query query, T item) { T[] array; int count; query.Deconstruct(out array, out count); var comparer = EqualityComparer.Default; for (int i = 0; i < count; i++) { if (comparer.Equals(item, array[i])) { ArrayPool.Recycle(array); return true; } } ArrayPool.Recycle(array); return false; } /// /// Returns the number of elements in the Query. /// public static int Count(this Query query) { using (var slice = query.Deconstruct()) { return slice.Count; } } /// /// Returns the number of elements in the Query that satisfy a predicate. /// public static int Count(this Query query, Func predicate) { return query.Where(predicate).Count(); } /// /// Counts the number of distinct elements in the Query. /// public static int CountUnique(this Query query) { var slice = query.Deconstruct(); var set = Pool>.Spawn(); try { for (int i = 0; i < slice.Count; i++) { set.Add(slice[i]); } return set.Count; } finally { slice.Dispose(); set.Clear(); Pool>.Recycle(set); } } /// /// Returns the number of distinct elements in the Query once it has been mapped /// using a selector function. /// public static int CountUnique(this Query query, Func selector) { return query.Select(selector).CountUnique(); } /// /// Returns the element at a specific index in the Query. Will throw an error /// if the Query has no element at that index. /// public static T ElementAt(this Query query, int index) { using (var slice = query.Deconstruct()) { if (index < 0 || index >= slice.Count) { throw new IndexOutOfRangeException("The index " + index + " was out of range. Query only has length of " + slice.Count); } return slice[index]; } } /// /// Returns the element at a specific index in the Query. Will return /// the default value if the Query has no element at that index. /// public static T ElementAtOrDefault(this Query query, int index) { using (var slice = query.Deconstruct()) { if (index < 0 || index >= slice.Count) { return default(T); } return slice[index]; } } /// /// Returns the first element in the Query. Will throw an error if there are /// no elements in the Query. /// public static T First(this Query query) { using (var slice = query.Deconstruct()) { if (slice.Count == 0) { throw new InvalidOperationException("The source Query was empty."); } return slice[0]; } } /// /// Returns the first element in the Query that satisfies a predicate. Will /// throw an error if there is no such element. /// public static T First(this Query query, Func predicate) { return query.Where(predicate).First(); } /// /// Returns the first element in the Query. Will return the default value /// if the Query is empty. /// public static T FirstOrDefault(this Query query) { using (var slice = query.Deconstruct()) { if (slice.Count == 0) { return default(T); } return slice[0]; } } /// /// Returns the first element in the Query that satisfies a predicate. Will return /// the default value if there is no such element. /// public static T FirstOrDefault(this Query query, Func predicate) { return query.Where(predicate).FirstOrDefault(); } /// /// Returns Some value that represents the first value in the Query, or None /// if there is no such value. /// public static Maybe FirstOrNone(this Query query) { using (var slice = query.Deconstruct()) { if (slice.Count == 0) { return Maybe.None; } return Maybe.Some(slice[0]); } } /// /// Returns the Some value representing the first value that satisfies the predicate, /// or None if there is no such value. /// public static Maybe FirstOrNone(this Query query, Func predicate) { return query.Where(predicate).FirstOrNone(); } /// /// Folds all of the elements in the Query into a single element, using a fold function. /// Will throw an error if there are no elements in the Query. /// /// The fold function takes in the current folded value, and the next item to fold in. /// It returns the result of folding the item into the current folded value. For example, /// you can use the Fold operation to implement a sum: /// /// var sum = numbers.Query().Fold((a,b) => a + b); /// public static T Fold(this Query query, Func foldFunc) { using (var slice = query.Deconstruct()) { if (slice.Count == 0) { throw new InvalidOperationException("The source Query was empty."); } var result = slice[0]; for (int i = 1; i < slice.Count; i++) { result = foldFunc(result, slice[i]); } return result; } } /// /// Returns the index of the first element that is equal to a specific value. Will return /// a negative index if there is no such element. /// public static int IndexOf(this Query query, T t) { using (var slice = query.Deconstruct()) { var comparer = EqualityComparer.Default; for (int i = 0; i < slice.Count; i++) { if (comparer.Equals(t, slice[i])) { return i; } } return -1; } } /// /// Returns the index of the first element to satisfy a predicate. Will return a negative /// index if there is no such element. /// public static int IndexOf(this Query query, Func predicate) { using (var slice = query.Deconstruct()) { for (int i = 0; i < slice.Count; i++) { if (predicate(slice[i])) { return i; } } return -1; } } /// /// Returns the last element in the Query. Will throw an error if the Query is empty. /// public static T Last(this Query query) { using (var slice = query.Deconstruct()) { if (slice.Count == 0) { throw new InvalidOperationException("The source Query was empty."); } else { return slice[slice.Count - 1]; } } } /// /// Returns the last element in the Query that satisfies a predicate. Will throw an error /// if there is no such element. /// public static T Last(this Query query, Func predicate) { return query.Where(predicate).Last(); } /// /// Returns the last element in the Query. Will return the default value if the Query is empty. /// public static T LastOrDefault(this Query query) { using (var slice = query.Deconstruct()) { if (slice.Count == 0) { return default(T); } else { return slice[slice.Count - 1]; } } } /// /// Returns the last element in the Query that satisfies a predicate. Will return /// the default value if there is no such element. /// public static T LastOrDefault(this Query query, Func predicate) { return query.Where(predicate).LastOrDefault(); } /// /// Returns Some value that represents the last value in the Query, or None /// if there is no such value. /// public static Maybe LastOrNone(this Query query) { using (var slice = query.Deconstruct()) { if (slice.Count == 0) { return Maybe.None; } else { return Maybe.Some(slice[slice.Count - 1]); } } } /// /// Returns the Some value representing the last value that satisfies the predicate, /// or None if there is no such value. /// public static Maybe LastOrNone(this Query query, Func predicate) { return query.Where(predicate).LastOrNone(); } /// /// Returns the largest element in the Query. /// public static T Max(this Query query) where T : IComparable { return query.Fold(FoldDelegate.max); } /// /// Returns the largest element in the Query after it has been mapped using a selector function. /// public static K Max(this Query query, Func selector) where K : IComparable { return query.Select(selector).Max(); } /// /// Returns the smallest element in the Query. /// public static T Min(this Query query) where T : IComparable { return query.Fold(FoldDelegate.min); } /// /// Returns the smallest element in the Query after it has been mapped using a selector function. /// public static K Min(this Query query, Func selector) where K : IComparable { return query.Select(selector).Min(); } /// /// Returns the first and only element in the Query. Will throw an error if the length of the /// Query is anything other than 1. /// public static T Single(this Query query) { using (var slice = query.Deconstruct()) { if (slice.Count != 1) { throw new InvalidOperationException("The Query had a count of " + slice.Count + " instead of a count of 1."); } else { return slice[0]; } } } /// /// Returns the first and only element in the Query that satisfies the predicate. Will throw /// an error if the number of such elements is anything other than 1. /// public static T Single(this Query query, Func predicate) { return query.Where(predicate).Single(); } /// /// Returns the first and only element in the Query. Will return the default value if the number /// of elements is anything other than 1. /// public static T SingleOrDefault(this Query query) { using (var slice = query.Deconstruct()) { if (slice.Count != 1) { return default(T); } else { return slice[0]; } } } /// /// Returns the first and only element in the Query that satisfies the predicate. Will return /// the default value if the number of such elements is anything other than 1. /// public static T SingleOrDefault(this Query query, Func predicate) { return query.Where(predicate).SingleOrDefault(); } /// /// Returns the first and only element in the Query. Will return None if the number /// of elements is anything other than 1. /// public static Maybe SingleOrNone(this Query query) { using (var slice = query.Deconstruct()) { if (slice.Count != 1) { return Maybe.None; } else { return Maybe.Some(slice[0]); } } } /// /// Returns the first and only element in the Query that satisfies the predicate. Will return /// None if the number of such elements is anything other than 1. /// public static Maybe SingleOrNone(this Query query, Func predicate) { return query.Where(predicate).SingleOrNone(); } /// /// Returns the sum of a Query of ints. /// public static int Sum(this Query query) { return query.Fold((a, b) => a + b); } /// /// Returns the sum of a Query of floats. /// public static float Sum(this Query query) { return query.Fold((a, b) => a + b); } /// /// Returns the sum of a Query of doubles. /// public static double Sum(this Query query) { return query.Fold((a, b) => a + b); } /// /// Returns the single value that is present in the entire Query. If there is more /// than one value in the Query or there are no values at all, this method will return /// the default value. /// public static T UniformOrDefault(this Query query) { return query.UniformOrNone().valueOrDefault; } /// /// Returns Some single value that is present in the entire Query. If there is more /// than one value in the Query or there are no values at all, this method will return /// None. /// public static Maybe UniformOrNone(this Query query) { using (var slice = query.Deconstruct()) { if (slice.Count == 0) { return Maybe.None; } var array = slice; T reference = array[0]; var comparer = EqualityComparer.Default; for (int i = 1; i < slice.Count; i++) { if (!comparer.Equals(reference, slice[i])) { return Maybe.None; } } return Maybe.Some(reference); } } /// /// Converts the Query into an array. /// public static T[] ToArray(this Query query) { using (var slice = query.Deconstruct()) { T[] result = new T[slice.Count]; Array.Copy(slice.BackingArray, result, slice.Count); return result; } } /// /// Copies the elements of the Query into an array. Can optionally specify the offset into the array /// where to copy. /// public static void FillArray(this Query query, T[] array, int offset = 0) { using (var slice = query.Deconstruct()) { Array.Copy(slice.BackingArray, 0, array, offset, slice.Count); } } /// /// Converts the Query into a list. /// public static List ToList(this Query query) { using (var slice = query.Deconstruct()) { List result = new List(slice.Count); for (int i = 0; i < slice.Count; i++) { result.Add(slice[i]); } return result; } } /// /// Fills a given list with the elements in this Query. The list is cleared before the fill happens. /// public static void FillList(this Query query, List list) { list.Clear(); query.AppendList(list); } /// /// Appends the elements in this Query to the end of a given list. /// public static void AppendList(this Query query, List list) { using (var slice = query.Deconstruct()) { for (int i = 0; i < slice.Count; i++) { list.Add(slice[i]); } } } /// /// Converts the Query into a HashSet. /// public static HashSet ToHashSet(this Query query) { HashSet set = new HashSet(); query.AppendHashSet(set); return set; } /// /// Fills a given HashSet with the elements in the Query. The set is cleared before the fill happens. /// public static void FillHashSet(this Query query, HashSet set) { set.Clear(); query.AppendHashSet(set); } /// /// Appends the elements in this Query into the given HashSet. /// public static void AppendHashSet(this Query query, HashSet set) { using (var slice = query.Deconstruct()) { for (int i = 0; i < slice.Count; i++) { set.Add(slice[i]); } } } /// /// Converts the Query into a Dictionary using a specific key selector and value selector. /// public static Dictionary ToDictionary(this Query query, Func keySelector, Func valueSelector) { using (var slice = query.Deconstruct()) { Dictionary dictionary = new Dictionary(); for (int i = 0; i < slice.Count; i++) { dictionary[keySelector(slice[i])] = valueSelector(slice[i]); } return dictionary; } } /// /// Converts the Query into a Dictionary using the query elements as keys, and using a value selector /// to select the value tied to the key. /// public static Dictionary ToDictionary(this Query query, Func valueSelector) { return query.ToDictionary(t => t, valueSelector); } private static class FoldDelegate where T : IComparable { public readonly static Func max = (a, b) => a.CompareTo(b) > 0 ? a : b; public readonly static Func min = (a, b) => a.CompareTo(b) < 0 ? a : b; } } }