Iterar matriz multidimensional con declaración Foreach anidada

79

Creo que esta puede ser una pregunta bastante simple, pero aún no he podido resolverla. Si tengo una matriz bidimensional como esta:

int[,] array = new int[2,3] { {1, 2, 3}, {4, 5, 6} };

¿Cuál es la mejor manera de iterar a través de cada dimensión de la matriz con una instrucción foreach anidada ?

Tyler Murry
fuente
¿Tiene que ser una matriz bidimensional o puede usar una matriz de matrices?
Matthew Flaschen

Respuestas:

133

Si desea iterar sobre cada elemento de la matriz como si fuera una matriz plana, puede hacer lo siguiente:

foreach (int i in array) {
    Console.Write(i);
}

que imprimiría

123456

Si desea poder conocer los índices xey también, deberá hacer lo siguiente:

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

Alternativamente, puede usar una matriz irregular en su lugar (una matriz de matrices):

int[][] array = new int[2][] { new int[3] {1, 2, 3}, new int[3] {4, 5, 6} };
foreach (int[] subArray in array) {
    foreach (int i in subArray) {
        Console.Write(i);
    }
}

o

int[][] array = new int[2][] { new int[3] {1, 2, 3}, new int[3] {4, 5, 6} };
for (int j = 0; j < array.Length; j += 1) {
    for (int k = 0; k < array[j].Length; k += 1) {
        Console.Write(array[j][k]);
    }
}
ICR
fuente
1
Después de ver el error de mis caminos (por ejemplo, tratando de usar una matriz 2D como una matriz irregular), estoy de acuerdo en que la mejor manera de recorrer esa lista es como la que tiene en su primer ejemplo. Obtienes el voto porque estás dispuesto a ir más allá de la respuesta y dar otras opciones.
Tyler Murry
¿Por qué se aplana la matriz de 2 dim?
Sipo
C # = siempre funciona. C ++ = no funcionará antes de perder el tiempo.
jw_
21

A continuación, se explica cómo visitar cada elemento en una matriz bidimensional. Esto es lo que estabas buscando?

for (int i=0;i<array.GetLength(0);i++)
{
    for (int j=0;j<array.GetLength(1);j++)
    {
        int cell = array[i,j];
    }
}
Daniel Plaisted
fuente
1
Esperaba una implementación de foreach , si es posible.
Tyler Murry
6

Con matrices multidimensionales, puede usar el mismo método para iterar a través de los elementos, por ejemplo:

int[,] numbers2D = new int[3, 2] { { 9, 99 }, { 3, 33 }, { 5, 55 } };
foreach (int i in numbers2D)
{
    System.Console.Write("{0} ", i);
}

El resultado de este ejemplo es:

9 99 3 33 5 55

Referencias


En Java, las matrices multidimensionales son matrices de matrices, por lo que funciona lo siguiente:

    int[][] table = {
            { 1, 2, 3 },
            { 4, 5, 6 },
    };
    for (int[] row : table) {
        for (int el : row) {
            System.out.println(el);
        }
    }
poligenelubricantes
fuente
3
C # tiene matrices dentadas y multidimensionales como conceptos separados, donde int[,]es una matriz de 2 dimensiones y int[][]es una matriz dentada de matrices y no se requiere que cada matriz dada tenga la misma longitud. Puede hacer fácilmente un foreach en la matriz dentada, pero una matriz 2D no es el mismo tipo de estructura. En cualquier caso, su segundo fragmento no se ajusta a este problema, el primer fragmento no está anidado.
Anthony Pegram
4

Sé que esta es una publicación antigua, pero la encontré a través de Google, y después de jugar con ella creo que tengo una solución más fácil. Si me equivoco, indíquelo, porque me gustaría saberlo, pero esto funcionó al menos para mis propósitos (se basa en la respuesta de ICR):

for (int x = 0; x < array.GetLength(0); x++)
{
    Console.Write(array[x, 0], array[x,1], array[x,2]);
}

Dado que ambas dimensiones son limitadas, cualquiera de las dos puede ser números simples y, por lo tanto, evitar un bucle for anidado. Admito que soy nuevo en C #, así que por favor, si hay alguna razón para no hacerlo, dígame ...

Keven M
fuente
1
Esto supone la longitud de la 2ª dimensión. Está codificando la suposición de que la segunda dimensión siempre tendrá una longitud de 3, lo que puede ser o no el caso. Si no es 3 pero, digamos, 50, estará buscando mucho copiar / pegar y hacer que su código sea ilegible. :)
Adam Lear
@AnnaLear La suposición no es necesariamente incorrecta, es posible que esté procesando una matriz de una longitud bien definida, como un tablero de scrabble de 15x15
Jaycee
3

La matriz 2D en C # no se presta bien a un foreach anidado, no es el equivalente a una matriz dentada (una matriz de matrices). Podrías hacer algo como esto para usar un foreach

foreach (int i in Enumerable.Range(0, array.GetLength(0)))
    foreach (int j in Enumerable.Range(0, array.GetLength(1)))
        Console.WriteLine(array[i, j]);

Pero aún usaría i y j como valores de índice para la matriz. La legibilidad se conservaría mejor si simplemente optara por el forbucle de variedad de jardín .

Anthony Pegram
fuente
1
Esta es una mutación horrible de una construcción clásica, simple y comprensible. Si su código contiene esto, realmente necesita estar pensando "oye, esto funciona, pero ... ¿qué tal si no?" (Soy muy consciente de que esto es idiomático en la familia de las pitones. Sin embargo, esto es C #.)
Rubys
@Rubys, esto tampoco es idiomático en Python. Es muy poco pitónico obtener los índices y luego acceder a la lista con []. Es idiomático Python iterar sobre los valores directamente. También tenga en cuenta que no hay matrices o listas multidimensionales en Python (solo irregulares).
Matthew Flaschen
@Matthew: Es posible que haya confundido Python con otro idioma, realmente no conozco mis brazos de mis piernas en la familia de secuencias de comandos (que es lo que quise decir en la familia Python, supongo)
Rubys
2

Dos caminos:

  1. Defina la matriz como una matriz dentada y use foreachs anidados.
  2. Defina la matriz normalmente y use foreach en todo.

Ejemplo de # 2:

int[,] arr = { { 1, 2 }, { 3, 4 } };
foreach(int a in arr)
    Console.Write(a);

La salida será 1234. es decir. exactamente lo mismo que hacer i de 0 an, y j de 0 an.

Rubíes
fuente
2

Puedes usar un método de extensión como este:

internal static class ArrayExt
{
    public static IEnumerable<int> Indices(this Array array, int dimension)
    {
        for (var i = array.GetLowerBound(dimension); i <= array.GetUpperBound(dimension); i++)
        {
            yield return i;
        }
    }
}

Y entonces:

int[,] array = { { 1, 2, 3 }, { 4, 5, 6 } };
foreach (var i in array.Indices(0))
{
    foreach (var j in array.Indices(1))
    {
        Console.Write(array[i, j]);
    }

    Console.WriteLine();
}

Será un poco más lento que usar bucles for, pero probablemente no sea un problema en la mayoría de los casos. No estoy seguro si hace las cosas más legibles.

Tenga en cuenta que las matrices de c # pueden ser distintas a las basadas en cero, por lo que puede usar un bucle for como este:

int[,] array = { { 1, 2, 3 }, { 4, 5, 6 } };
for (var i = array.GetLowerBound(0); i <= array.GetUpperBound(0); i++)
{
    for (var j= array.GetLowerBound(1); j <= array.GetUpperBound(1); j++)
    {
        Console.Write(array[i, j]);
    }

    Console.WriteLine();
}
Johan Larsson
fuente
raro ... ¿qué no es de base cero?
drzaus
Creo que vi no basado en cero cuando interopeé con Excel, no es común pero quizás sea bueno tenerlo en cuenta.
Johan Larsson
1

Como se mencionó en otra parte, puede iterar sobre la matriz y producirá todos los resultados en orden en todas las dimensiones. Sin embargo, si también desea conocer los índices, ¿qué tal si usa esto? Http://blogs.msdn.com/b/ericlippert/archive/2010/06/28/computing-a-cartesian-product-with- linq.aspx

luego haciendo algo como:

var dimensionLengthRanges = Enumerable.Range(0, myArray.Rank).Select(x => Enumerable.Range(0, myArray.GetLength(x)));
var indicesCombinations = dimensionLengthRanges.CartesianProduct();

foreach (var indices in indicesCombinations)
{
    Console.WriteLine("[{0}] = {1}", string.Join(",", indices), myArray.GetValue(indices.ToArray()));
}
Daniel Smith
fuente
1

Utilice LINQ .Cast<int>()para convertir una matriz 2D a IEnumerable<int>.

Ejemplo de LINQPad:

var arr = new int[,] { 
  { 1, 2, 3 }, 
  { 4, 5, 6 } 
};

IEnumerable<int> values = arr.Cast<int>();
Console.WriteLine(values);

Salida:

La secuencia es 1,2,3,4,5,6

angularsen
fuente
0

También puede utilizar enumeradores. Cada tipo de matriz de cualquier dimensión admite el método Array.GetEnumerator. La única advertencia es que tendrás que lidiar con el boxeo / unboxing. Sin embargo, el código que necesita escribir será bastante trivial.

Aquí está el código de muestra:

class Program
{
    static void Main(string[] args)
    {
        int[,] myArray = new int[,] { { 1, 2 }, { 3, 4 }, { 5, 6 } };
        var e = myArray.GetEnumerator();

        e.Reset();

        while (e.MoveNext())
        {
            // this will output each number from 1 to 6. 
            Console.WriteLine(e.Current.ToString());
        }

        Console.ReadLine();
    }
}
code4life
fuente
Voto -1 ... Cito msdnUsing foreach is recommended, instead of directly manipulating the enumerator.
Ivan
0
int[,] arr =  { 
                {1, 2, 3},
                {4, 5, 6},
                {7, 8, 9}
              };
 for(int i = 0; i < arr.GetLength(0); i++){
      for (int j = 0; j < arr.GetLength(1); j++)
           Console.Write( "{0}\t",arr[i, j]);
      Console.WriteLine();
    }

output:  1  2  3
         4  5  6
         7  8  9
Sohel Ahmed
fuente
0

Estaba buscando una solución para enumerar una matriz de un rango desconocido en el tiempo de compilación con acceso a cada conjunto de índices de elementos. Vi soluciones con rendimiento, pero aquí hay otra implementación sin rendimiento. Es al estilo minimalista de la vieja escuela. En este ejemplo AppendArrayDebug () simplemente imprime todos los elementos en el búfer StringBuilder.

public static void AppendArrayDebug ( StringBuilder sb, Array array )
{
    if( array == null || array.Length == 0 )
    {
        sb.Append( "<nothing>" );
        return;
    }

    int i;

    var rank = array.Rank;
    var lastIndex = rank - 1;

    // Initialize indices and their boundaries
    var indices = new int[rank];
    var lower = new int[rank];
    var upper = new int[rank];
    for( i = 0; i < rank; ++i )
    {
        indices[i] = lower[i] = array.GetLowerBound( i );
        upper[i] = array.GetUpperBound( i );
    }

    while( true )
    {
        BeginMainLoop:

        // Begin work with an element

        var element = array.GetValue( indices );

        sb.AppendLine();
        sb.Append( '[' );
        for( i = 0; i < rank; ++i )
        {
            sb.Append( indices[i] );
            sb.Append( ' ' );
        }
        sb.Length -= 1;
        sb.Append( "] = " );
        sb.Append( element );

        // End work with the element

        // Increment index set

        // All indices except the first one are enumerated several times
        for( i = lastIndex; i > 0; )
        {
            if( ++indices[i] <= upper[i] )
                goto BeginMainLoop;
            indices[i] = lower[i];
            --i;
        }

        // Special case for the first index, it must be enumerated only once
        if( ++indices[0] > upper[0] )
            break;
    }
}

Por ejemplo, la siguiente matriz producirá la siguiente salida:

var array = new [,,]
{
    { {  1,  2,  3 }, {  4,  5,  6 }, {  7,  8,  9 }, { 10, 11, 12 } },
    { { 13, 14, 15 }, { 16, 17, 18 }, { 19, 20, 21 }, { 22, 23, 24 } }
};

/*
Output:

[0 0 0] = 1
[0 0 1] = 2
[0 0 2] = 3
[0 1 0] = 4
[0 1 1] = 5
[0 1 2] = 6
[0 2 0] = 7
[0 2 1] = 8
[0 2 2] = 9
[0 3 0] = 10
[0 3 1] = 11
[0 3 2] = 12
[1 0 0] = 13
[1 0 1] = 14
[1 0 2] = 15
[1 1 0] = 16
[1 1 1] = 17
[1 1 2] = 18
[1 2 0] = 19
[1 2 1] = 20
[1 2 2] = 21
[1 3 0] = 22
[1 3 1] = 23
[1 3 2] = 24
*/
C0DEF52
fuente