-1

I need to know How to modify or how to initialize all element of 2d array without For loop?
I mean how to do it using Extension methods or Using LINQ!?

I was trying to do it using "IEnumerable.Cast<>" extension but there is no result!
And I don`t know why?

        string[,] m2d = new string[8, 8];

        Array.ForEach(m2d.Cast<string>().ToArray(), el => el = "sample1");

There was no result even with for loop...

        for (int i = 0; i <= m2d.Cast<string>().ToArray().GetUpperBound(0); i++)
        {
            m2d.Cast<string>().ToArray()[i] = "sample2";
        }

But please forget this for loop!
Just try to do it using one line expression!
like this one which does not work...

        m2d.Cast<string>().ToList().ForEach(el => el = "sample3");

Thank you!

  • 1
    "how to do it using Extension methods" -- well, how about: `public static class ArrayExtensions { public static void SetAllValuesTo(this string[,] array, string value) { for (var i = 0; i < array.GetLength(0); i++) { for (var j = 0; j < array.GetLength(1); j++) { array[i, j] = value; } } } }`? And then call it like `array.SetAllValuesTo("sample");`. – Corak Apr 17 '16 at 12:49

2 Answers2

1

It doesn't work because with assignment you just replace value in a collection created by ToList() or ToArray() method with a new one. Because both these methods actually return new collection, your starting array isn't affected by the change you're making.

The most obvious way, of course, is to use two nested for loops. Not sure why you avoid them but If you really want to use ForEach you could enumerate indices of your array's dimensions, and not it's elements, in kind of a functional approach. Something like this:

Enumerable.Range(0, m2d.GetUpperBound(0) + 1).ToList()
            .ForEach(i => Enumerable.Range(0, m2d.GetUpperBound(1) + 1).ToList()
                .ForEach(j => m2d[i, j] = "sample"));
Dmitry Rotay
  • 3,260
  • 1
  • 15
  • 11
0

While @Dmitry's answer pretty much covers why the original attempt has failed and what you should do to rectify, nonetheless, depending on your requirements, you may also want to consider wrapping each item/index in the array into some referential type, that is able to change the original array items:

public class MultiDimensionArrayItemReference<T>
{
    private readonly Array _array;
    private readonly Int32[] _indices;

    public MultiDimensionArrayItemReference(Array array, params Int32[] indices)
    {
        if (array == null)
            throw new ArgumentNullException(paramName: nameof(array));
        if (indices == null)
            throw new ArgumentNullException(paramName: nameof(indices));
        this._array = array;
        this._indices = indices;
    }

    public IReadOnlyCollection<Int32> Indices
    {
        get
        {
            return this._indices.ToList().AsReadOnly();
        }
    }

    public T Value
    {
        get
        {
            return (T)this._array.GetValue(this._indices);
        }
        set
        {
            this._array.SetValue(value, this._indices);
        }
    }

    public override string ToString()
    {
        return $"[{String.Join(", ", this._indices)}]:{this.Value}";
    }
}


public static class MultiDimensionArrayItemReferenceExtensions
{
    // That's for the two-dimensional array, but with some effort it can be generalized to support any arrays.
    public static IEnumerable<MultiDimensionArrayItemReference<T>> EnumerateReferenceElements<T>(this T[,] array)
    {
        if (array == null)
            throw new ArgumentNullException(paramName: nameof(array));

        // Assume zero-based
        var rows = array.GetLength(0);
        var columns = array.GetLength(1);

        for (int row = 0; row < rows; row++)
        { 
            for (int col = 0; col < columns; col++)
            {
                yield return new MultiDimensionArrayItemReference<T>(
                    array,
                    row,
                    col);
            }
        }
    }
}

...

private static void PrintArray<T>(T[,] array)
{
    // Assume zero-based
    var rows = array.GetLength(0);
    var columns = array.GetLength(1);

    for (int row = 0; row < rows; row++)
    {
        for (int col = 0; col < columns; col++)
        {
            Console.Write("{0, 7}", array[row, col]);
        }
        Console.WriteLine();
    }
}

...

var array = new [,]
{
    { "a1", "a2" },
    { "b1", "b2" }
};

PrintArray(array);
Console.WriteLine();

var elements = array
    .EnumerateReferenceElements()
    .ToList();

foreach (var elem in elements)
{
    Console.WriteLine(elem);
}

elements.ForEach(
    elem =>
        elem.Value = elem.Value + "_n");

Console.WriteLine();
PrintArray(array);

It will result in the following output:

     a1     a2
     b1     b2

[0, 0]:a1
[0, 1]:a2
[1, 0]:b1
[1, 1]:b2

   a1_n   a2_n
   b1_n   b2_n

It is not very efficient due to the need to store each item's indices, but still a possibile solution for some rare cases.

Community
  • 1
  • 1
Eugene Podskal
  • 10,270
  • 5
  • 31
  • 53