/******************************************************************************
* 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;
}
}
}