Add IReadOnlyList<T>.IndexOf

This commit is contained in:
Oliver Booth 2022-12-01 12:28:33 +00:00
parent 9c2f77b203
commit e02fd3b1b4
No known key found for this signature in database
GPG Key ID: 32A00B35503AF634
5 changed files with 212 additions and 1 deletions

View File

@ -20,6 +20,7 @@
- X10D: Added `IEnumerable<T>.WhereNot(Func<T, bool>)`
- X10D: Added `IList<T>.RemoveRange(Range)`
- X10D: Added `IList<T>.Swap(IList<T>)` (#62)
- X10D: Added `IReadOnlyList<T>.IndexOf(T[, int[, int]])`
- X10D: Added `Point.IsOnLine(LineF)`, `Point.IsOnLine(PointF, PointF)`, and `Point.IsOnLine(Vector2, Vector2)`
- X10D: Added `PointF.IsOnLine(LineF)`, `PointF.IsOnLine(PointF, PointF)`, and `PointF.IsOnLine(Vector2, Vector2)`
- X10D: Added `Point.ToSize()`

View File

@ -79,6 +79,67 @@ public class ListTests
Assert.ThrowsException<ArgumentNullException>(() => list!.Fill(0, 0, 0));
}
[TestMethod]
public void IndexOf_ShouldReturnCorrectValue_FromStartOfList()
{
int[] array = {0, 1, 2, 3, 4};
Assert.AreEqual(2, array.IndexOf(2));
Assert.AreEqual(2, array.IndexOf(2, 0));
Assert.AreEqual(2, array.IndexOf(2, 0, 5));
}
[TestMethod]
public void IndexOf_ShouldReturnCorrectValue_GivenSubRange()
{
int[] array = {0, 1, 2, 3, 4, 0};
Assert.AreEqual(0, array.IndexOf(0));
Assert.AreEqual(0, array.IndexOf(0, 0));
Assert.AreEqual(0, array.IndexOf(0, 0, 5));
Assert.AreEqual(5, array.IndexOf(0, 1));
Assert.AreEqual(5, array.IndexOf(0, 1, 5));
}
[TestMethod]
public void IndexOf_ShouldReturnNegative1_ForEmptyList()
{
int[] array = Array.Empty<int>();
Assert.AreEqual(-1, array.IndexOf(0));
Assert.AreEqual(-1, array.IndexOf(0, 0));
Assert.AreEqual(-1, array.IndexOf(0, 0, 0));
}
[TestMethod]
public void IndexOf_ShouldThrowArgumentNullException_GivenNullList()
{
int[]? array = null;
Assert.ThrowsException<ArgumentNullException>(() => array!.IndexOf(0));
Assert.ThrowsException<ArgumentNullException>(() => array!.IndexOf(0, 0));
Assert.ThrowsException<ArgumentNullException>(() => array!.IndexOf(0, 0, 0));
}
[TestMethod]
public void IndexOf_ShouldThrowArgumentOutOfRangeException_GivenNegativeCount()
{
int[] array = Array.Empty<int>();
Assert.ThrowsException<ArgumentOutOfRangeException>(() => array.IndexOf(0, 0, -1));
}
[TestMethod]
public void IndexOf_ShouldThrowArgumentOutOfRangeException_GivenNegativeStartIndex()
{
int[] array = Array.Empty<int>();
Assert.ThrowsException<ArgumentOutOfRangeException>(() => array.IndexOf(0, -1));
Assert.ThrowsException<ArgumentOutOfRangeException>(() => array.IndexOf(0, -1, 0));
}
[TestMethod]
public void IndexOf_ShouldThrowArgumentOutOfRangeException_GivenInvalidStartIndexCountPair()
{
int[] array = {0, 1, 2};
Assert.ThrowsException<ArgumentOutOfRangeException>(() => array.IndexOf(0, 2, 4));
}
[TestMethod]
public void Random_ShouldReturnContainedObject_GivenNotNull()
{

View File

@ -1,3 +1,4 @@
using System.Diagnostics;
using System.Diagnostics.Contracts;
using X10D.Core;
@ -88,6 +89,131 @@ public static class ListExtensions
}
}
/// <summary>
/// Searches for the specified object and returns the zero-based index of the first occurrence within the entire
/// <see cref="IReadOnlyList{T}" />.
/// </summary>
/// <param name="source">The list to search</param>
/// <param name="item">
/// The object to locate in the <see cref="IReadOnlyList{T}" />. The value can be <see langword="true" /> for reference
/// types.
/// </param>
/// <typeparam name="T">The type of elements in <paramref name="source" />.</typeparam>
/// <returns>
/// The zero-based index of the first occurrence of item within the entire <see cref="List{T}" />, if found; otherwise,
/// -1.
/// </returns>
/// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
public static int IndexOf<T>(this IReadOnlyList<T?> source, T? item)
{
#if NET6_0_OR_GREATER
ArgumentNullException.ThrowIfNull(source);
#else
if (source is null)
{
throw new ArgumentNullException(nameof(source));
}
#endif
return source.IndexOf(item, 0, source.Count);
}
/// <summary>
/// Searches for the specified object and returns the zero-based index of the first occurrence within the range of
/// elements in the <see cref="IReadOnlyList{T}" /> that extends from the specified index to the last element.
/// </summary>
/// <param name="source">The list to search</param>
/// <param name="item">
/// The object to locate in the <see cref="IReadOnlyList{T}" />. The value can be <see langword="true" /> for reference
/// types.
/// </param>
/// <param name="startIndex">The zero-based starting index of the search. 0 (zero) is valid in an empty list.</param>
/// <typeparam name="T">The type of elements in <paramref name="source" />.</typeparam>
/// <returns>
/// The zero-based index of the first occurrence of item within the range of elements in the
/// <see cref="IReadOnlyList{T}" /> that starts at index and contains count number of elements, if found; otherwise, -1.
/// </returns>
/// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
/// <exception cref="ArgumentOutOfRangeException">
/// <paramref name="startIndex" /> is outside the range of valid indexes for the <see cref="IReadOnlyList{T}" />.
/// </exception>
public static int IndexOf<T>(this IReadOnlyList<T?> source, T? item, int startIndex)
{
#if NET6_0_OR_GREATER
ArgumentNullException.ThrowIfNull(source);
#else
if (source is null)
{
throw new ArgumentNullException(nameof(source));
}
#endif
return source.IndexOf(item, startIndex, source.Count - startIndex);
}
/// <summary>
/// Searches for the specified object and returns the zero-based index of the first occurrence within the range of
/// elements in the <see cref="IReadOnlyList{T}" /> that starts at the specified index and contains the specified number
/// of elements.
/// </summary>
/// <param name="source">The list to search</param>
/// <param name="item">
/// The object to locate in the <see cref="IReadOnlyList{T}" />. The value can be <see langword="true" /> for reference
/// types.
/// </param>
/// <param name="startIndex">The zero-based starting index of the search. 0 (zero) is valid in an empty list.</param>
/// <param name="count">The number of elements in the section to search.</param>
/// <typeparam name="T">The type of elements in <paramref name="source" />.</typeparam>
/// <returns>
/// The zero-based index of the first occurrence of item within the range of elements in the
/// <see cref="IReadOnlyList{T}" /> that starts at index and contains count number of elements, if found; otherwise, -1.
/// </returns>
/// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
/// <exception cref="ArgumentOutOfRangeException">
/// <para>
/// <paramref name="startIndex" /> is outside the range of valid indexes for the <see cref="IReadOnlyList{T}" />.
/// </para>
/// -or-
/// <para><paramref name="count" /> is less than 0.</para>
/// -or-
/// <para>
/// <paramref name="startIndex" /> and <paramref name="count" /> do not specify a valid section in the
/// <see cref="IReadOnlyList{T}" />.
/// </para>
/// </exception>
public static int IndexOf<T>(this IReadOnlyList<T?> source, T? item, int startIndex, int count)
{
#if NET6_0_OR_GREATER
ArgumentNullException.ThrowIfNull(source);
#else
if (source is null)
{
throw new ArgumentNullException(nameof(source));
}
#endif
if (startIndex < 0 || startIndex > source.Count)
{
throw new ArgumentOutOfRangeException(nameof(startIndex), ExceptionMessages.IndexOutOfRange);
}
if (count < 0 || count > source.Count - startIndex)
{
throw new ArgumentOutOfRangeException(nameof(count), ExceptionMessages.CountMustBeInRange);
}
int endIndex = startIndex + count;
for (int index = startIndex; index < endIndex; index++)
{
if (EqualityComparer<T>.Default.Equals(source[index]!, item!))
{
return index;
}
}
return -1;
}
/// <summary>
/// Returns a random element from the current list using a specified <see cref="System.Random" /> instance.
/// </summary>

View File

@ -1,7 +1,6 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
@ -78,6 +77,15 @@ namespace X10D {
}
}
/// <summary>
/// Looks up a localized string similar to Count must be positive and count must refer to a location within the string/array/collection..
/// </summary>
internal static string CountMustBeInRange {
get {
return ResourceManager.GetString("CountMustBeInRange", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to The end index must be less than the list count..
/// </summary>
@ -114,6 +122,15 @@ namespace X10D {
}
}
/// <summary>
/// Looks up a localized string similar to Index was out of range. Must be non-negative and less than or equal to the size of the collection..
/// </summary>
internal static string IndexOutOfRange {
get {
return ResourceManager.GetString("IndexOutOfRange", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Length must be greater than or equal to 0..
/// </summary>

View File

@ -26,6 +26,9 @@
<data name="BufferTooSmall" xml:space="preserve">
<value>The buffer is too small to contain the data.</value>
</data>
<data name="CountMustBeInRange" xml:space="preserve">
<value>Count must be positive and count must refer to a location within the string/array/collection.</value>
</data>
<data name="EndIndexLessThanStartIndex" xml:space="preserve">
<value>The end index must be greater than or equal to the start index.</value>
</data>
@ -47,6 +50,9 @@
<data name="HashAlgorithmCreateReturnedNull" xml:space="preserve">
<value>HashAlgorithm's Create method returned null reference.</value>
</data>
<data name="IndexOutOfRange" xml:space="preserve">
<value>Index was out of range. Must be non-negative and less than or equal to the size of the collection.</value>
</data>
<data name="LengthGreaterThanOrEqualTo0" xml:space="preserve">
<value>Length must be greater than or equal to 0.</value>
</data>