using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
namespace Dalamud.Utility;
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1618:Generic type parameters should be documented", Justification = "Reviewed,")]
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Reviewed,")]
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1615:Element return value should be documented", Justification = "Reviewed,")]
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1611:Element parameters should be documented", Justification = "Reviewed,")]
internal static class ArrayExtensions
{
/// Iterate over enumerables with additional index.
public static IEnumerable<(T Value, int Index)> WithIndex(this IEnumerable list)
=> list.Select((x, i) => (x, i));
/// Remove an added index from an indexed enumerable.
public static IEnumerable WithoutIndex(this IEnumerable<(T Value, int Index)> list)
=> list.Select(x => x.Value);
/// Remove the value and only keep the index from an indexed enumerable.
public static IEnumerable WithoutValue(this IEnumerable<(T Value, int Index)> list)
=> list.Select(x => x.Index);
// Find the index of the first object fulfilling predicate's criteria in the given list.
// Returns -1 if no such object is found.
public static int IndexOf(this IEnumerable array, Predicate predicate)
{
var i = 0;
foreach (var obj in array)
{
if (predicate(obj))
return i;
++i;
}
return -1;
}
// Find the index of the first occurrence of needle in the given list.
// Returns -1 if needle is not contained in the list.
public static int IndexOf(this IEnumerable array, T needle) where T : notnull
{
var i = 0;
foreach (var obj in array)
{
if (needle.Equals(obj))
return i;
++i;
}
return -1;
}
// Find the first object fulfilling predicate's criteria in the given list, if one exists.
// Returns true if an object is found, false otherwise.
public static bool FindFirst(this IEnumerable array, Predicate predicate, [NotNullWhen(true)] out T? result)
{
foreach (var obj in array)
{
if (predicate(obj))
{
result = obj!;
return true;
}
}
result = default;
return false;
}
// Find the first occurrence of needle in the given list and return the value contained in the list in result.
// Returns true if an object is found, false otherwise.
public static bool FindFirst(this IEnumerable array, T needle, [NotNullWhen(true)] out T? result) where T : notnull
{
foreach (var obj in array)
{
if (obj.Equals(needle))
{
result = obj;
return true;
}
}
result = default;
return false;
}
///
/// Interprets the given array as an , so that you can enumerate it multiple
/// times, and know the number of elements within.
///
/// The enumerable.
/// The element type.
/// casted as a if it is one; otherwise the result of .
public static IReadOnlyCollection AsReadOnlyCollection(this IEnumerable array) =>
array as IReadOnlyCollection ?? array.ToArray();
///
public static int FindIndex(this IReadOnlyList list, Predicate match)
=> list.FindIndex(0, list.Count, match);
///
public static int FindIndex(this IReadOnlyList list, int startIndex, Predicate match)
=> list.FindIndex(startIndex, list.Count - startIndex, match);
///
public static int FindIndex(this IReadOnlyList list, int startIndex, int count, Predicate match)
{
if ((uint)startIndex > (uint)list.Count)
throw new ArgumentOutOfRangeException(nameof(startIndex), startIndex, null);
if (count < 0 || startIndex > list.Count - count)
throw new ArgumentOutOfRangeException(nameof(count), count, null);
if (match == null)
throw new ArgumentNullException(nameof(match));
var endIndex = startIndex + count;
for (var i = startIndex; i < endIndex; i++)
{
if (match(list[i])) return i;
}
return -1;
}
///
public static int FindLastIndex(this IReadOnlyList list, Predicate match)
=> list.FindLastIndex(list.Count - 1, list.Count, match);
///
public static int FindLastIndex(this IReadOnlyList list, int startIndex, Predicate match)
=> list.FindLastIndex(startIndex, startIndex + 1, match);
///
public static int FindLastIndex(this IReadOnlyList list, int startIndex, int count, Predicate match)
{
if (match == null)
throw new ArgumentNullException(nameof(match));
if (list.Count == 0)
{
// Special case for 0 length List
if (startIndex != -1)
throw new ArgumentOutOfRangeException(nameof(startIndex), startIndex, null);
}
else
{
// Make sure we're not out of range
if ((uint)startIndex >= (uint)list.Count)
throw new ArgumentOutOfRangeException(nameof(startIndex), startIndex, null);
}
// 2nd have of this also catches when startIndex == MAXINT, so MAXINT - 0 + 1 == -1, which is < 0.
if (count < 0 || startIndex - count + 1 < 0)
throw new ArgumentOutOfRangeException(nameof(count), count, null);
var endIndex = startIndex - count;
for (var i = startIndex; i > endIndex; i--)
{
if (match(list[i]))
{
return i;
}
}
return -1;
}
}