Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Anvil2DExtension - Add #107

Merged
merged 3 commits into from
Aug 24, 2022
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
161 changes: 161 additions & 0 deletions Data/Collections/Util/Array2DExtension.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
using System;

namespace Anvil.CSharp.Data
{
//TODO: #104 - Optimize
/// <summary>
/// A set of helper extension methods for 2D arrays
/// </summary>
public static class Array2DExtension
{
/// <summary>
/// Gets the value at the specified index or returns the default value if out of range.
/// </summary>
/// <param name="array">The array to query.</param>
/// <param name="x">The first order index</param>
/// <param name="y">The second order index</param>
/// <typeparam name="T">The type of the array.</typeparam>
/// <returns>The value at the specified index or default.</returns>
public static T GetElementOrDefaultAt<T>(this T[,] array, int x, int y)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would an optional explicit default value be useful? Mostly for primitives I think, so you could say array.GetElementOrDefaultAt(x, y, -1);

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. I don't think that's inline with the other *OrDefault methods but that's fine. This could be useful in the right situation.

{
int limitX = array.GetLength(0);
int limitY = array.GetLength(1);

if (x >= limitX || y >= limitY || x < 0 || y < 0)
{
return default;
}

return array[x, y];
}

/// <summary>
/// Gets the index of the first element in the array that satisfies the specified <see cref="Predicate{T}"/>.
/// </summary>
/// <param name="array">The array to query.</param>
/// <param name="predicate">
/// The <see cref="Predicate{T}"/> to call for each element. If the predicate returns true the method returns
/// the current index.
/// </param>
/// <typeparam name="T">The type of the array.</typeparam>
/// <returns>The first index that satisfied the predicate.</returns>
public static (int,int) IndexOf<T>(this T[,] array, Predicate<T> predicate)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest another name (maybe FindIndexOf<T>?) to indicate a conditional search - IndexOf<T> makes me think I'm just passing an element/value and asking for its index in the array.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call. I'll follow the lead of Linq and Array and name the method FindIndex

{
for (int y = 0; y < array.GetLength(1); y++)
{
for (int x = 0; x < array.GetLength(0); x++)
{
if (predicate(array[x, y]))
{
return (x, y);
}
}
}

return (-1, -1);
}

/// <summary>
/// Iterates over the array and calls the specified <see cref="Action{T}"/> for each element.
/// </summary>
/// <param name="array">The array to iterate.</param>
/// <param name="action">The action to call on each element.</param>
/// <typeparam name="T">The type of the array.</typeparam>
public static void ForEach<T>(this T[,] array, Action<T> action)
{
array.ForEach((element, x, y) => action(element));
}

/// <summary>
/// Iterates over the array and calls the specified <see cref="Action{T,int,int}"/> for each element.
/// The current index is provided to the action.
/// </summary>
/// <param name="array">The array to iterate.</param>
/// <param name="action">The action to call on each element.</param>
/// <typeparam name="T">The type of the array.</typeparam>
public static void ForEach<T>(this T[,] array, Action<T, int, int> action)
{
for (int y = 0; y < array.GetLength(1); y++)
{
for (int x = 0; x < array.GetLength(0); x++)
{
action(array[x, y], x, y);
}
}
}

/// <summary>
/// Iterates over the array and calls the specified <see cref="Func{int,int,T}"/> to set each element.
/// The current index is provided to the function.
/// </summary>
/// <param name="array">The array to iterate and populate.</param>
/// <param name="createAction">
/// The function that, provided first and second order indices returns a value to assign to those indices.
/// </param>
/// <typeparam name="T">The type of the array.</typeparam>
public static void Populate<T>(this T[,] array, Func<int, int, T> createAction)
{
for (int y = 0; y < array.GetLength(1); y++)
{
for (int x = 0; x < array.GetLength(0); x++)
{
array[x, y] = createAction(x, y);
}
}
}

/// <summary>
/// Determines whether the specified <see cref="Func{T,int,int,bool}"/> is true for any element in the array.
/// The element as well as indices are provided to the function.
/// </summary>
/// <param name="array">The array to evaluate.</param>
/// <param name="condition">
/// The function that, provided element value and indices, evaluates a condition. If it returns true the method
/// returns true.
/// </param>
/// <typeparam name="T">The type of the array.</typeparam>
/// <returns>true if <see cref="condition"/> evaluates to true for any element.</returns>
public static bool Any<T>(this T[,] array, Func<T, int, int, bool> condition)
{
for (int y = 0; y < array.GetLength(1); y++)
{
for (int x = 0; x < array.GetLength(0); x++)
{
if(condition(array[x, y], x, y))
{
return true;
}
}
}

return false;
}

/// <summary>
/// Determines whether the specified <see cref="Func{T,int,int,bool}"/> is true for all elements in the array.
/// The element as well as indices are provided to the function.
/// </summary>
/// <param name="array">The array to evaluate.</param>
/// <param name="condition">
/// The function that, provided element value and indices, evaluates a condition. If it returns false the method
/// returns false.
/// </param>
/// <typeparam name="T">The type of the array.</typeparam>
/// <returns>true if <see cref="condition"/> evaluates to true for all elements.</returns>
public static bool All<T>(this T[,] array, Func<T, int, int, bool> condition)
{
return !array.Any((element, x, y) => !condition(element, x, y));
}

/// <summary>
/// Gets the two dimensional length of the array.
/// </summary>
/// <param name="array">The array to evaluate.</param>
/// <typeparam name="T">The type of the array.</typeparam>
/// <returns>The first and second order lengths of the array.</returns>
public static (int,int) GetLength<T>(this T[,] array)
{
return (array.GetLength(0), array.GetLength(1));
}
}
}