Iteruj tablicę wielowymiarową za pomocą zagnieżdżonej instrukcji Foreach

79

Myślę, że to może być całkiem proste pytanie, ale nie byłem jeszcze w stanie tego rozgryźć. Jeśli mam dwuwymiarową tablicę, taką jak ta:

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

Jaki jest najlepszy sposób na iterację przez każdy wymiar tablicy za pomocą zagnieżdżonej instrukcji foreach ?

Tyler Murry
źródło
Czy musi to być tablica dwuwymiarowa, czy możesz użyć tablicy tablic?
Matthew Flaschen

Odpowiedzi:

133

Jeśli chcesz iterować po każdym elemencie w tablicy, tak jakby to była spłaszczona tablica, możesz po prostu zrobić:

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

które by się wydrukowało

123456

Jeśli chcesz znać również indeksy x i y, musisz zrobić:

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]);
    }
}

Alternatywnie możesz zamiast tego użyć tablicy postrzępionej (tablicy tablic):

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);
    }
}

lub

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
źródło
1
Widząc mój błąd (np. Próbując użyć tablicy 2D, takiej jak tablicy postrzępionej), zgadzam się, że najlepszy sposób na iterację tej listy jest taki, jak w pierwszym przykładzie. Otrzymujesz głos, ponieważ chcesz wyjść poza odpowiedź i podać inne opcje.
Tyler Murry
Dlaczego matryca o 2 wymiarach jest spłaszczona?
Sipo
C # = to zawsze po prostu działa. C ++ = nie zadziała, zanim stracisz trochę czasu.
jw_
21

Oto jak odwiedzić każdy element w dwuwymiarowej tablicy. Czy tego szukałeś?

for (int i=0;i<array.GetLength(0);i++)
{
    for (int j=0;j<array.GetLength(1);j++)
    {
        int cell = array[i,j];
    }
}
Daniel grał
źródło
1
Liczyłem na wdrożenie foreach , jeśli to możliwe?
Tyler Murry
6

W przypadku tablic wielowymiarowych możesz użyć tej samej metody do iteracji po elementach, na przykład:

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

Dane wyjściowe tego przykładu to:

9 99 3 33 5 55

Bibliografia


W Javie tablice wielowymiarowe są tablicami tablic, więc działa:

    int[][] table = {
            { 1, 2, 3 },
            { 4, 5, 6 },
    };
    for (int[] row : table) {
        for (int el : row) {
            System.out.println(el);
        }
    }
smary wielogenowe
źródło
3
C # ma wielowymiarowe i postrzępione tablice jako oddzielne koncepcje, gdzie int[,]jest to dwuwymiarowa tablica i int[][]jest postrzępioną tablicą tablic, a każda podana tablica nie musi mieć tej samej długości. Możesz łatwo wykonać foreach na tablicy postrzępionej, ale tablica 2D nie jest tym samym typem struktury. W każdym razie Twój drugi fragment nie pasuje do tego problemu, pierwszy fragment nie jest zagnieżdżony.
Anthony Pegram
4

Wiem, że to stary post, ale znalazłem go przez Google i po zabawie z nim myślę, że mam łatwiejsze rozwiązanie. Jeśli się mylę, wskaż to, bo chciałbym wiedzieć, ale to zadziałało przynajmniej dla moich celów (jest oparte na odpowiedzi ICR):

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

Ponieważ oba wymiary są ograniczone, każdy z nich może być prostymi liczbami, a tym samym uniknąć zagnieżdżonej pętli for. Przyznaję, że jestem nowy w C #, więc proszę, jeśli jest powód, aby tego nie robić, powiedz mi ...

Keven M.
źródło
1
To zakłada długość drugiego wymiaru. Zakodowujesz założenie, że drugi wymiar zawsze będzie miał długość 3, co może, ale nie musi, mieć miejsce. Jeśli nie jest to 3, ale powiedzmy 50, będziesz patrzeć na wiele operacji kopiowania / wklejania i sprawiając, że kod będzie nieczytelny. :)
Adam Lear
@AnnaLear Założenie niekoniecznie jest źle, może być przetwarzanie macierzy o dobrze określonej długości, takie jak deska 15x15 scrabble
Jaycee
3

Tablica 2D w języku C # nie nadaje się dobrze do zagnieżdżonego foreach, nie jest odpowiednikiem tablicy postrzępionej (tablicy tablic). Możesz zrobić coś takiego, aby użyć 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]);

Ale nadal używałbyś i i j jako wartości indeksu dla tablicy. Czytelność byłaby lepiej zachowana, gdyby forzamiast tego wybrałeś pętlę odmian ogrodowych .

Anthony Pegram
źródło
1
To straszna mutacja klasycznej, prostej i zrozumiałej konstrukcji. Jeśli twój kod to zawiera, naprawdę musisz pomyśleć „hej, to działa, ale ... a co z nie?” (Zdaję sobie sprawę, że jest to idiomatyczne w rodzinie pytonów. Jednak - to jest C #.)
Rubys
@Rubys, to też nie jest idiomatyczne w Pythonie. Pobieranie indeksów, a następnie dostęp do listy za pomocą []. Idiomatycznym Pythonem jest bezpośrednie iterowanie po wartościach. Należy również zauważyć, że w Pythonie nie ma wielowymiarowych tablic ani list (tylko postrzępione).
Matthew Flaschen
@Matthew: Mogłem pomylić Pythona z innym językiem, tak naprawdę nie znam moich rąk z nóg w rodzinie skryptów (co chyba miałem na myśli w rodzinie pythona)
Rubys
2

Dwie drogi:

  1. Zdefiniuj tablicę jako tablicę postrzępioną i użyj zagnieżdżonych foreach.
  2. Zdefiniuj tablicę normalnie i używaj foreach na całej rzeczy.

Przykład nr 2:

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

Wyjście będzie 1234. tj. dokładnie to samo, co robienie i od 0 do n oraz j od 0 do n.

Rubiny
źródło
2

Możesz użyć następującej metody rozszerzenia:

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;
        }
    }
}

I wtedy:

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();
}

Będzie to trochę wolniejsze niż używanie pętli for, ale w większości przypadków prawdopodobnie nie stanowi problemu. Nie jestem pewien, czy to czyni rzeczy bardziej czytelnymi.

Zauważ, że tablice w języku C # mogą być inne niż od zera, więc możesz użyć pętli for w następujący sposób:

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
źródło
dziwne ... co nie jest oparte na zerach?
drzaus
Wydaje mi się, że podczas współpracy z programem Excel widziałem wartość niezerową, nie jest to powszechne, ale być może warto o tym wiedzieć.
Johan Larsson
1

Jak wspomniano w innym miejscu, możesz po prostu iterować po tablicy, a wszystkie wyniki zostaną wyświetlone w kolejności we wszystkich wymiarach. Jeśli jednak chcesz poznać także indeksy, to co powiesz na to - http://blogs.msdn.com/b/ericlippert/archive/2010/06/28/computing-a-cartesian-product-with- linq.aspx

następnie robiąc coś takiego:

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
źródło
1

Użyj LINQ, .Cast<int>()aby przekonwertować tablicę 2D na IEnumerable<int>.

Przykład LINQPad:

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

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

Wynik:

Sekwencja to 1, 2, 3, 4, 5, 6

angularsen
źródło
0

Możesz także użyć modułów wyliczających. Każdy typ tablicy dowolnego wymiaru obsługuje metodę Array.GetEnumerator. Jedynym zastrzeżeniem jest to, że będziesz musiał poradzić sobie z boksowaniem / rozpakowywaniem. Jednak kod, który musisz napisać, będzie dość trywialny.

Oto przykładowy kod:

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
źródło
Głosowanie -1 ... Cytuję 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
źródło
0

Szukałem rozwiązania do wyliczenia tablicy nieznanej w czasie kompilacji rangi z dostępem do każdego zestawu indeksów elementów. Widziałem rozwiązania z wydajnością, ale oto kolejna implementacja bez wydajności. Jest w oldschoolowy, minimalistyczny sposób. W tym przykładzie AppendArrayDebug () po prostu drukuje wszystkie elementy do bufora 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;
    }
}

Na przykład poniższa tablica wygeneruje następujące dane wyjściowe:

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
źródło