Add ultimate xr
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="CollectionExt.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UltimateXR.Extensions.System.Collections
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="IReadOnlyCollection{T}" /> and <see cref="ICollection{T}" /> extensions.
|
||||
/// </summary>
|
||||
public static class CollectionExt
|
||||
{
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Throws an exception if a given index is out of a <see cref="IReadOnlyCollection{T}" /> bounds.
|
||||
/// </summary>
|
||||
/// <param name="self">Collection</param>
|
||||
/// <param name="index">Index to check if it is out of bounds</param>
|
||||
/// <param name="paramName">Optional argument name</param>
|
||||
/// <typeparam name="T">Element type</typeparam>
|
||||
/// <exception cref="IndexOutOfRangeException">When index is out of range and no parameter name was specified</exception>
|
||||
/// <exception cref="ArgumentOutOfRangeException">When index is out of range and a parameter name was specified</exception>
|
||||
public static void ThrowIfInvalidIndex<T>(this IReadOnlyCollection<T> self, int index, string paramName = null)
|
||||
{
|
||||
if (index >= 0 && index < self.Count)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(paramName))
|
||||
{
|
||||
throw new IndexOutOfRangeException($"Index[{index}] out of range for collection of {typeof(T).Name}");
|
||||
}
|
||||
throw new ArgumentOutOfRangeException(paramName, index, $"Index[{index}] out of range for collection of {typeof(T).Name}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Throws an exception if any of the given indexes is out of a <see cref="IReadOnlyCollection{T}" /> bounds.
|
||||
/// </summary>
|
||||
/// <param name="self">Collection</param>
|
||||
/// <param name="index1">Index 1 to check if it is out of bounds</param>
|
||||
/// <param name="index2">Index 2 to check if it is out of bounds</param>
|
||||
/// <typeparam name="T">Element type</typeparam>
|
||||
/// <seealso cref="ThrowIfInvalidIndex{T}" />
|
||||
public static void ThrowIfInvalidIndexes<T>(this IReadOnlyCollection<T> self, int index1, int index2)
|
||||
{
|
||||
self.ThrowIfInvalidIndex(index1);
|
||||
self.ThrowIfInvalidIndex(index2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Throws an exception if any of the given indexes is out of a <see cref="IReadOnlyCollection{T}" /> bounds.
|
||||
/// </summary>
|
||||
/// <param name="self">Collection</param>
|
||||
/// <param name="index1">Index 1 to check if it is out of bounds</param>
|
||||
/// <param name="index2">Index 2 to check if it is out of bounds</param>
|
||||
/// <param name="index3">Index 3 to check if it is out of bounds</param>
|
||||
/// <typeparam name="T">Element type</typeparam>
|
||||
/// <seealso cref="ThrowIfInvalidIndex{T}" />
|
||||
public static void ThrowIfInvalidIndexes<T>(this IReadOnlyCollection<T> self, int index1, int index2, int index3)
|
||||
{
|
||||
self.ThrowIfInvalidIndex(index1);
|
||||
self.ThrowIfInvalidIndex(index2);
|
||||
self.ThrowIfInvalidIndex(index3);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Throws an exception if any of the given indexes is out of a <see cref="IReadOnlyCollection{T}" /> bounds.
|
||||
/// </summary>
|
||||
/// <param name="self">Collection</param>
|
||||
/// <param name="indexes">Indexes to check</param>
|
||||
/// <typeparam name="T">Element type</typeparam>
|
||||
public static void ThrowIfInvalidIndexes<T>(this IReadOnlyCollection<T> self, params int[] indexes)
|
||||
{
|
||||
foreach (int index in indexes)
|
||||
{
|
||||
self.ThrowIfInvalidIndex(index);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Splits a string using <see cref="string.Split(char[])" /> and adds the result to the collection.
|
||||
/// </summary>
|
||||
/// <param name="self">Collection to add the split result to</param>
|
||||
/// <param name="toSplit">String to split</param>
|
||||
/// <param name="separator">
|
||||
/// Separator to use for splitting. This will be used to call <see cref="string.Split(char[])" />
|
||||
/// on <paramref name="toSplit" />
|
||||
/// </param>
|
||||
/// <returns>The result collection</returns>
|
||||
public static ICollection<string> SplitAddRange(this ICollection<string> self, string toSplit, char separator)
|
||||
{
|
||||
self.ThrowIfNull(nameof(self));
|
||||
|
||||
if (string.IsNullOrWhiteSpace(toSplit))
|
||||
{
|
||||
return self;
|
||||
}
|
||||
|
||||
foreach (string s in toSplit.Split(separator))
|
||||
{
|
||||
self.Add(s.Trim());
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Splits a string using <see cref="string.Split(char[])" /> and sets the result in the collection.
|
||||
/// </summary>
|
||||
/// <param name="self">Collection to set the split result in</param>
|
||||
/// <param name="toSplit">String to split</param>
|
||||
/// <param name="separator">
|
||||
/// Separator to use for splitting. This will be used to call <see cref="string.Split(char[])" />
|
||||
/// on <paramref name="toSplit" />
|
||||
/// </param>
|
||||
/// <returns>The result collection</returns>
|
||||
public static ICollection<string> SplitSetRange(this ICollection<string> self, string toSplit, char separator)
|
||||
{
|
||||
self.ThrowIfNull(nameof(self));
|
||||
self.Clear();
|
||||
return self.SplitAddRange(toSplit, separator);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2fbb5811e6ee84042ad95c9df2447cfd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,66 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="DictionaryExt.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UltimateXR.Extensions.System.Collections
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="IDictionary{TKey,TValue}" /> and <see cref="Dictionary{TKey,TValue}" /> extensions.
|
||||
/// </summary>
|
||||
public static class DictionaryExt
|
||||
{
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Adds all elements in another dictionary to the dictionary.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">Key type</typeparam>
|
||||
/// <typeparam name="TValue">Value type</typeparam>
|
||||
/// <param name="self">Destination dictionary</param>
|
||||
/// <param name="other">Source dictionary</param>
|
||||
/// <param name="overrideExistingKeys">Determines if duplicated keys must be overriden with other's values</param>
|
||||
public static void AddRange<TKey, TValue>(this IDictionary<TKey, TValue> self, IDictionary<TKey, TValue> other, bool overrideExistingKeys = false)
|
||||
{
|
||||
foreach (var kv in other)
|
||||
{
|
||||
if (!self.ContainsKey(kv.Key))
|
||||
{
|
||||
self.Add(kv.Key, kv.Value);
|
||||
}
|
||||
else if (overrideExistingKeys)
|
||||
{
|
||||
self[kv.Key] = kv.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a given value defined by a key in a dictionary. If the key is not found, it is added and the value is given
|
||||
/// the default value.
|
||||
/// </summary>
|
||||
/// <param name="self">Dictionary</param>
|
||||
/// <param name="key">Key to look for</param>
|
||||
/// <typeparam name="TKey">Key type</typeparam>
|
||||
/// <typeparam name="TValue">Value type</typeparam>
|
||||
/// <returns>
|
||||
/// Value in the dictionary. If the key doesn't exist it will be added and the return value will be the default
|
||||
/// value
|
||||
/// </returns>
|
||||
public static TValue GetOrAddValue<TKey, TValue>(this IDictionary<TKey, TValue> self, TKey key)
|
||||
where TValue : new()
|
||||
{
|
||||
if (!self.TryGetValue(key, out TValue value))
|
||||
{
|
||||
value = new TValue();
|
||||
self.Add(key, value);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7d0cdca585a749e69c9d098ba98c006d
|
||||
timeCreated: 1620807425
|
||||
@@ -0,0 +1,318 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="EnumerableExt.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
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
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="IEnumerable{T}" /> extensions.
|
||||
/// </summary>
|
||||
public static class EnumerableExt
|
||||
{
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Compares two IEnumerable for equality, considering the order of elements.
|
||||
/// For dictionaries, compares key-value pairs regardless of their order.
|
||||
/// </summary>
|
||||
/// <param name="enumerableA">The first collection to compare</param>
|
||||
/// <param name="enumerableB">The second collection to compare</param>
|
||||
/// <returns>True if the collections are equal; otherwise, false</returns>
|
||||
public static bool ContentEqual(IEnumerable enumerableA, IEnumerable enumerableB)
|
||||
{
|
||||
return ContentEqual(enumerableA, enumerableB, (a, b) => a.ValuesEqual(b));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// <see cref="ObjectExt.ValuesEqual(object,object,float)" />.
|
||||
/// </summary>
|
||||
/// <param name="enumerableA">The first collection to compare</param>
|
||||
/// <param name="enumerableB">The second collection to compare</param>
|
||||
/// <param name="precisionThreshold">
|
||||
/// The precision threshold for float comparisons in types supported by
|
||||
/// <see cref="ObjectExt.ValuesEqual(object,object,float)" />.
|
||||
/// </param>
|
||||
/// <returns>True if the collections are equal; otherwise, false</returns>
|
||||
public static bool ContentEqual(IEnumerable enumerableA, IEnumerable enumerableB, float precisionThreshold)
|
||||
{
|
||||
return ContentEqual(enumerableA, enumerableB, (a, b) => a.ValuesEqual(b, precisionThreshold));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a random element from the collection.
|
||||
/// </summary>
|
||||
/// <param name="list">Collection to get the random element from</param>
|
||||
/// <typeparam name="TIn">Element type</typeparam>
|
||||
/// <returns>Random element from the collection</returns>
|
||||
/// <remarks>
|
||||
/// Uses Unity's random number generator (<see cref="UnityEngine.Random.Range(int,int)" />).
|
||||
/// </remarks>
|
||||
public static TIn RandomElement<TIn>(this IEnumerable<TIn> list)
|
||||
{
|
||||
return list.Any() ? list.ElementAt(Random.Range(0, list.Count())) : default(TIn);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies an <see cref="Action" /> on all elements in a collection.
|
||||
/// </summary>
|
||||
/// <param name="list">Elements to apply the action on</param>
|
||||
/// <param name="action">Action to apply</param>
|
||||
/// <typeparam name="TIn">Element type</typeparam>
|
||||
/// <exception cref="ArgumentException">Any of the parameters was null</exception>
|
||||
public static void ForEach<TIn>(this IEnumerable<TIn> list, Action<TIn> 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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously applies a function on all elements in a collection.
|
||||
/// </summary>
|
||||
/// <param name="list">Elements to apply the function on</param>
|
||||
/// <param name="function">Function to apply</param>
|
||||
/// <typeparam name="TIn">Element type</typeparam>
|
||||
/// <returns>An awaitable task wrapping the Task.WhenAll applying the function on all elements in a collection</returns>
|
||||
public static Task ForEachAsync<TIn>(this IEnumerable<TIn> list, Func<TIn, Task> function)
|
||||
{
|
||||
return Task.WhenAll(list.Select(function));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously applies a function to all elements in a collection.
|
||||
/// </summary>
|
||||
/// <param name="list">Elements to apply the function on</param>
|
||||
/// <param name="function">Function to apply</param>
|
||||
/// <typeparam name="TIn">Element type</typeparam>
|
||||
/// <typeparam name="TOut">Function return type</typeparam>
|
||||
/// <returns>An awaitable task wrapping the Task.WhenAll applying the function on all elements in a collection</returns>
|
||||
public static Task<TOut[]> ForEachAsync<TIn, TOut>(this IEnumerable<TIn> list, Func<TIn, Task<TOut>> function)
|
||||
{
|
||||
return Task.WhenAll(list.Select(function));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously applies an action on all elements in a collection.
|
||||
/// </summary>
|
||||
/// <param name="list">Elements to apply the action on</param>
|
||||
/// <param name="action">Action to apply</param>
|
||||
/// <typeparam name="TIn">Element type</typeparam>
|
||||
/// <returns>Task wrapping the Task.WhenAll applying the action on all elements in a collection</returns>
|
||||
public static Task ForEachThreaded<TIn>(this IEnumerable<TIn> list, Action<TIn> 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)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously applies a function on all elements in a collection.
|
||||
/// </summary>
|
||||
/// <param name="list">Elements to apply the function on</param>
|
||||
/// <param name="function">Function to apply</param>
|
||||
/// <typeparam name="TIn">Element type</typeparam>
|
||||
/// <typeparam name="TOut">Function return type</typeparam>
|
||||
/// <returns>Task wrapping the Task.WhenAll applying the function on all elements in a collection</returns>
|
||||
public static Task<TOut[]> ForEachThreaded<TIn, TOut>(this IEnumerable<TIn> list, Func<TIn, TOut> function)
|
||||
{
|
||||
TOut OnFaulted(Task<TOut> 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)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the maximal element of the given sequence, based on the given projection.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
/// <typeparam name="TSource">Type of the source sequence</typeparam>
|
||||
/// <typeparam name="TKey">Type of the projected element</typeparam>
|
||||
/// <param name="source">Source sequence</param>
|
||||
/// <param name="selector">Selector to use to pick the results to compare</param>
|
||||
/// <returns>The maximal element, according to the projection.</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="source" /> or <paramref name="selector" /> is null</exception>
|
||||
public static TSource MaxBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> selector)
|
||||
{
|
||||
return source.MaxBy(selector, null!);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the maximal element of the given sequence, based on the given projection and the specified comparer for
|
||||
/// projected values.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This operator uses deferred execution. The results are evaluated and cached on first use to returned sequence.
|
||||
/// </remarks>
|
||||
/// <typeparam name="TSource">Type of the source sequence</typeparam>
|
||||
/// <typeparam name="TKey">Type of the projected element</typeparam>
|
||||
/// <param name="source">Source sequence</param>
|
||||
/// <param name="selector">Selector to use to pick the results to compare</param>
|
||||
/// <param name="comparer">Comparer to use to compare projected values</param>
|
||||
/// <returns>The maximal element, according to the projection.</returns>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// <paramref name="source" />, <paramref name="selector" /> or <paramref name="comparer" /> is null
|
||||
/// </exception>
|
||||
/// <exception cref="T:System.Exception">A delegate callback throws an exception.</exception>
|
||||
public static TSource MaxBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> selector, IComparer<TKey> comparer)
|
||||
{
|
||||
source.ThrowIfNull(nameof(source));
|
||||
selector.ThrowIfNull(nameof(selector));
|
||||
|
||||
TSource result = default;
|
||||
TKey keyMax = default;
|
||||
comparer ??= Comparer<TKey>.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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Splits a list of strings using CamelCase.
|
||||
/// </summary>
|
||||
/// <param name="strings">List of strings</param>
|
||||
/// <returns>List of strings with added spacing</returns>
|
||||
public static IEnumerable<string> SplitCamelCase(this IEnumerable<string> strings)
|
||||
{
|
||||
foreach (string element in strings)
|
||||
{
|
||||
yield return element.SplitCamelCase();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Compares two IEnumerable for equality, considering the order of elements.
|
||||
/// For dictionaries, compares key-value pairs regardless of their order.
|
||||
/// </summary>
|
||||
/// <param name="enumerableA">The first collection to compare</param>
|
||||
/// <param name="enumerableB">The second collection to compare</param>
|
||||
/// <param name="comparer">Comparison function</param>
|
||||
/// <returns>True if the collections are equal; otherwise, false</returns>
|
||||
private static bool ContentEqual(IEnumerable enumerableA, IEnumerable enumerableB, Func<object, object, bool> comparer)
|
||||
{
|
||||
// If the collections are dictionaries, compare key-value pairs
|
||||
if (enumerableA is IDictionary dictionaryA && enumerableB is IDictionary dictionaryB)
|
||||
{
|
||||
// Ensure both dictionaries have the same number of elements
|
||||
if (dictionaryA.Count != dictionaryB.Count)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Compare key-value pairs regardless of order
|
||||
foreach (DictionaryEntry entryA in dictionaryA)
|
||||
{
|
||||
if (!dictionaryB.Contains(entryA.Key) || !comparer(entryA.Value, dictionaryB[entryA.Key]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the collections are lists, do a quick test to check if they have different number of elements
|
||||
if (enumerableA is IList listA && enumerableB is IList listB)
|
||||
{
|
||||
if (listA.Count != listB.Count)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// If the collections are HashSets, compare elements using SetEquals
|
||||
if (enumerableA is HashSet<object> hashSetA && enumerableB is HashSet<object> hashSetB)
|
||||
{
|
||||
return hashSetA.SetEquals(hashSetB);
|
||||
}
|
||||
|
||||
// For non-dictionary, non-HashSet collections, compare elements
|
||||
IEnumerator enumeratorA = enumerableA.GetEnumerator();
|
||||
IEnumerator enumeratorB = enumerableB.GetEnumerator();
|
||||
|
||||
while (enumeratorA.MoveNext())
|
||||
{
|
||||
if (!enumeratorB.MoveNext() || !comparer(enumeratorA.Current, enumeratorB.Current))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure both collections have the same number of elements
|
||||
return !enumeratorB.MoveNext();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: aded6c93d8bb43a799907d1f47eca9a4
|
||||
timeCreated: 1586946987
|
||||
@@ -0,0 +1,161 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="ListExt.cs" company="VRMADA">
|
||||
// Copyright (c) VRMADA, All rights reserved.
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UltimateXR.Extensions.System.Collections
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="List{T}" /> extensions.
|
||||
/// </summary>
|
||||
public static class ListExt
|
||||
{
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Gets the index of a given item in a list.
|
||||
/// </summary>
|
||||
/// <param name="self">List where to look for the item</param>
|
||||
/// <param name="item">Item to look for</param>
|
||||
/// <typeparam name="T">Element type</typeparam>
|
||||
/// <returns>Element index or -1 if not found</returns>
|
||||
/// <remarks>Equals() is used for comparison</remarks>
|
||||
public static int IndexOf<T>(this IReadOnlyList<T> self, T item)
|
||||
{
|
||||
for (int i = 0; i < self.Count; ++i)
|
||||
{
|
||||
if (Equals(self[i], item))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the list in reverse order.
|
||||
/// </summary>
|
||||
/// <param name="self">List in reverse order</param>
|
||||
/// <typeparam name="T">Element type</typeparam>
|
||||
/// <returns>List in reverse order</returns>
|
||||
public static IEnumerable<T> Reversed<T>(this IList<T> self)
|
||||
{
|
||||
for (int i = self.Count - 1; i >= 0; --i)
|
||||
{
|
||||
yield return self[i];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a random element from the list.
|
||||
/// </summary>
|
||||
/// <param name="self">List to get the random element from</param>
|
||||
/// <typeparam name="T">Element type</typeparam>
|
||||
/// <returns>Random element from the list</returns>
|
||||
/// <remarks>
|
||||
/// Uses Unity's random number generator (<see cref="Random.Range(int,int)" />).
|
||||
/// </remarks>
|
||||
public static T RandomElement<T>(this IReadOnlyList<T> self)
|
||||
{
|
||||
return self.Count > 0 ? self[Random.Range(0, self.Count)] : default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a list of random indices without repetition in a given range.
|
||||
/// </summary>
|
||||
/// <param name="minInclusive">The minimum index value (inclusive)</param>
|
||||
/// <param name="maxExclusive">The maximum value, which will not be included</param>
|
||||
/// <param name="count"></param>
|
||||
/// <returns></returns>
|
||||
public static List<int> GenerateRandomIndicesWithoutRepetition(int minInclusive, int maxExclusive, int count)
|
||||
{
|
||||
List<int> newList = new List<int>();
|
||||
|
||||
for (int i = minInclusive; i < maxExclusive; ++i)
|
||||
{
|
||||
newList.Add(i);
|
||||
}
|
||||
|
||||
return newList.RandomElementsWithoutRepetition(count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list with n random elements from a list without repetition.
|
||||
/// </summary>
|
||||
/// <param name="self">List to get the random elements from</param>
|
||||
/// <param name="count">Number of random elements to get</param>
|
||||
/// <typeparam name="T">Element type</typeparam>
|
||||
/// <returns>
|
||||
/// List with random elements. If <paramref name="count" /> is larger than the list, the result list will be as
|
||||
/// long as the input list.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// Uses Unity's random number generator (<see cref="Random.Range(int,int)" />).
|
||||
/// </remarks>
|
||||
public static List<T> RandomElementsWithoutRepetition<T>(this IReadOnlyList<T> self, int count)
|
||||
{
|
||||
List<T> candidates = new List<T>(self);
|
||||
List<T> randomElements = new List<T>();
|
||||
|
||||
for (int i = 0; i < count && candidates.Count > 0; ++i)
|
||||
{
|
||||
int randomIndex = Random.Range(0, candidates.Count);
|
||||
randomElements.Add(candidates[randomIndex]);
|
||||
candidates.RemoveAt(randomIndex);
|
||||
}
|
||||
|
||||
return randomElements;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list with n random elements from a list without repetition. An additional list can be provided to exclude
|
||||
/// elements from appearing in the results.
|
||||
/// </summary>
|
||||
/// <param name="self">List to get the random elements from</param>
|
||||
/// <param name="listToExclude">List of elements to exclude from the results</param>
|
||||
/// <param name="count">Number of random elements to get</param>
|
||||
/// <typeparam name="T">Element type</typeparam>
|
||||
/// <returns>
|
||||
/// List with random elements. If <paramref name="count" /> is larger than the list, the result list will be as
|
||||
/// long as the input list minus the excluded elements.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// Uses Unity's random number generator (<see cref="Random.Range(int,int)" />).
|
||||
/// </remarks>
|
||||
public static List<T> RandomElementsWithoutRepetitionExcept<T>(this IReadOnlyList<T> self, IReadOnlyList<T> listToExclude, int count)
|
||||
{
|
||||
List<T> candidates = new List<T>(self.Where(p => !listToExclude.Any(p2 => Equals(p2, p))));
|
||||
List<T> randomElements = new List<T>();
|
||||
|
||||
for (int i = 0; i < count && candidates.Count > 0; ++i)
|
||||
{
|
||||
int randomIndex = Random.Range(0, candidates.Count);
|
||||
randomElements.Add(candidates[randomIndex]);
|
||||
candidates.RemoveAt(randomIndex);
|
||||
}
|
||||
|
||||
return randomElements;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list with the input list elements shuffled.
|
||||
/// </summary>
|
||||
/// <param name="self">List to get the random elements from</param>
|
||||
/// <typeparam name="T">Element type</typeparam>
|
||||
/// <returns>List with shuffled elements.</returns>
|
||||
/// <remarks>
|
||||
/// Uses Unity's random number generator (<see cref="Random.Range(int,int)" />).
|
||||
/// </remarks>
|
||||
public static List<T> Shuffled<T>(this IReadOnlyList<T> self)
|
||||
{
|
||||
return self.RandomElementsWithoutRepetition(self.Count);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0fe3095d1ef7a9c428f15cc9a190b4b2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user