// --------------------------------------------------------------------------------------------------------------------
//
// Copyright (c) VRMADA, All rights reserved.
//
// --------------------------------------------------------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using UltimateXR.Core;
using UltimateXR.Core.Settings;
using UnityEngine;
using Random = UnityEngine.Random;
namespace UltimateXR.Extensions.System.Collections
{
///
/// extensions.
///
public static class EnumerableExt
{
#region Public Methods
///
/// Compares two IEnumerable for equality, considering the order of elements.
/// For dictionaries, compares key-value pairs regardless of their order.
///
/// The first collection to compare
/// The second collection to compare
/// True if the collections are equal; otherwise, false
public static bool ContentEqual(IEnumerable enumerableA, IEnumerable enumerableB)
{
return ContentEqual(enumerableA, enumerableB, (a, b) => a.ValuesEqual(b));
}
///
/// Compares two IEnumerable for equality, considering the order of elements.
/// For dictionaries, compares key-value pairs regardless of their order.
/// Values are compared using a floating point precision threshold used by
/// .
///
/// The first collection to compare
/// The second collection to compare
///
/// The precision threshold for float comparisons in types supported by
/// .
///
/// True if the collections are equal; otherwise, false
public static bool ContentEqual(IEnumerable enumerableA, IEnumerable enumerableB, float precisionThreshold)
{
return ContentEqual(enumerableA, enumerableB, (a, b) => a.ValuesEqual(b, precisionThreshold));
}
///
/// Returns a random element from the collection.
///
/// Collection to get the random element from
/// Element type
/// Random element from the collection
///
/// Uses Unity's random number generator ().
///
public static TIn RandomElement(this IEnumerable list)
{
return list.Any() ? list.ElementAt(Random.Range(0, list.Count())) : default(TIn);
}
///
/// Applies an on all elements in a collection.
///
/// Elements to apply the action on
/// Action to apply
/// Element type
/// Any of the parameters was null
public static void ForEach(this IEnumerable list, Action action)
{
if (list == null)
{
throw new ArgumentException("Argument cannot be null.", nameof(list));
}
if (action == null)
{
throw new ArgumentException("Argument cannot be null.", nameof(action));
}
foreach (TIn value in list)
{
action(value);
}
}
///
/// Asynchronously applies a function on all elements in a collection.
///
/// Elements to apply the function on
/// Function to apply
/// Element type
/// An awaitable task wrapping the Task.WhenAll applying the function on all elements in a collection
public static Task ForEachAsync(this IEnumerable list, Func function)
{
return Task.WhenAll(list.Select(function));
}
///
/// Asynchronously applies a function to all elements in a collection.
///
/// Elements to apply the function on
/// Function to apply
/// Element type
/// Function return type
/// An awaitable task wrapping the Task.WhenAll applying the function on all elements in a collection
public static Task ForEachAsync(this IEnumerable list, Func> function)
{
return Task.WhenAll(list.Select(function));
}
///
/// Asynchronously applies an action on all elements in a collection.
///
/// Elements to apply the action on
/// Action to apply
/// Element type
/// Task wrapping the Task.WhenAll applying the action on all elements in a collection
public static Task ForEachThreaded(this IEnumerable list, Action action)
{
void OnFaulted(Task runTask, int itemIndex)
{
if (UxrGlobalSettings.Instance.LogLevelCore >= UxrLogLevel.Warnings)
{
Debug.LogWarning($"{UxrConstants.CoreModule} ForEachThreaded::Item[{itemIndex}] faulted (see reason below):");
}
if (UxrGlobalSettings.Instance.LogLevelCore >= UxrLogLevel.Errors)
{
Debug.LogException(runTask.Exception);
}
}
return Task.WhenAll(list.Select((item, index) => Task.Run(() => action(item)).ContinueWith(runTask => OnFaulted(runTask, index), TaskContinuationOptions.OnlyOnFaulted)));
}
///
/// Asynchronously applies a function on all elements in a collection.
///
/// Elements to apply the function on
/// Function to apply
/// Element type
/// Function return type
/// Task wrapping the Task.WhenAll applying the function on all elements in a collection
public static Task ForEachThreaded(this IEnumerable list, Func function)
{
TOut OnFaulted(Task t, int itemIndex)
{
if (UxrGlobalSettings.Instance.LogLevelCore >= UxrLogLevel.Warnings)
{
Debug.LogWarning($"{UxrConstants.CoreModule} ForEachThreaded::Item[{itemIndex}] faulted (see reason below):");
}
if (UxrGlobalSettings.Instance.LogLevelCore >= UxrLogLevel.Errors)
{
Debug.LogException(t.Exception);
}
return default;
}
return Task.WhenAll(list.Select((item, index) => Task.Run(() => function(item)).ContinueWith(t => OnFaulted(t, index), TaskContinuationOptions.OnlyOnFaulted)));
}
///
/// Returns the maximal element of the given sequence, based on the given projection.
///
///
/// This overload uses the default comparer for the projected type. This operator uses deferred execution. The results
/// are evaluated and cached on first use to returned sequence.
///
/// Type of the source sequence
/// Type of the projected element
/// Source sequence
/// Selector to use to pick the results to compare
/// The maximal element, according to the projection.
/// or is null
public static TSource MaxBy(this IEnumerable source, Func selector)
{
return source.MaxBy(selector, null!);
}
///
/// Returns the maximal element of the given sequence, based on the given projection and the specified comparer for
/// projected values.
///
///
/// This operator uses deferred execution. The results are evaluated and cached on first use to returned sequence.
///
/// Type of the source sequence
/// Type of the projected element
/// Source sequence
/// Selector to use to pick the results to compare
/// Comparer to use to compare projected values
/// The maximal element, according to the projection.
///
/// , or is null
///
/// A delegate callback throws an exception.
public static TSource MaxBy(this IEnumerable source, Func selector, IComparer comparer)
{
source.ThrowIfNull(nameof(source));
selector.ThrowIfNull(nameof(selector));
TSource result = default;
TKey keyMax = default;
comparer ??= Comparer.Default;
bool isFirst = true;
foreach (TSource s in source)
{
TKey key = selector(s);
if (isFirst)
{
result = s;
keyMax = key;
isFirst = false;
}
else if (comparer.Compare(key, keyMax) > 0)
{
result = s;
keyMax = key;
}
}
return result;
}
///
/// Splits a list of strings using CamelCase.
///
/// List of strings
/// List of strings with added spacing
public static IEnumerable SplitCamelCase(this IEnumerable strings)
{
foreach (string element in strings)
{
yield return element.SplitCamelCase();
}
}
#endregion
#region Private Methods
///
/// Compares two IEnumerable for equality, considering the order of elements.
/// For dictionaries, compares key-value pairs regardless of their order.
///
/// The first collection to compare
/// The second collection to compare
/// Comparison function
/// True if the collections are equal; otherwise, false
private static bool ContentEqual(IEnumerable enumerableA, IEnumerable enumerableB, Func