Итерировать многомерный массив с помощью вложенного оператора Foreach

79

Я думаю, что это может быть довольно простой вопрос, но я еще не смог его понять. Если у меня есть такой двумерный массив:

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

Какой лучший способ перебрать каждое измерение массива с помощью вложенного оператора foreach ?

Тайлер Мерри
источник
Должен ли он быть двумерным массивом или можно использовать массив массивов?
Мэтью Флашен,

Ответы:

133

Если вы хотите перебрать каждый элемент в массиве, как если бы это был плоский массив, вы можете просто сделать:

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

который напечатает

123456

Если вы хотите знать индексы x и y, вам необходимо сделать:

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

В качестве альтернативы вы можете использовать вместо этого зубчатый массив (массив массивов):

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

или же

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
источник
1
Увидев ошибку в моих способах (например, попытку использовать 2D-массив как массив с зазубринами), я согласен, что лучший способ перебирать этот список - такой же, как в вашем первом примере. Вы получаете голос из-за вашей готовности выйти за рамки ответа и предложить другие варианты.
Тайлер Марри
Почему двухмерный массив сплющен?
Sipo
C # = всегда просто работает. C ++ = не будет работать, пока вы не потратите время.
jw_
21

Вот как можно посетить каждый элемент двумерного массива. Это то, что ты искал?

for (int i=0;i<array.GetLength(0);i++)
{
    for (int j=0;j<array.GetLength(1);j++)
    {
        int cell = array[i,j];
    }
}
Дэниел Плейстед
источник
1
Я надеялся на реализацию foreach , если это возможно?
Тайлер Мерри
6

С многомерными массивами вы можете использовать тот же метод для перебора элементов, например:

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

Результат этого примера:

9 99 3 33 5 55

Рекомендации


В Java многомерные массивы представляют собой массивы массивов, поэтому работает следующее:

    int[][] table = {
            { 1, 2, 3 },
            { 4, 5, 6 },
    };
    for (int[] row : table) {
        for (int el : row) {
            System.out.println(el);
        }
    }
полигенные смазочные материалы
источник
3
В C # есть многомерные и зубчатые массивы как отдельные концепции, где int[,]- это двухмерный массив и int[][]зубчатый массив массивов, и каждый заданный массив не обязательно должен иметь одинаковую длину. Вы можете легко выполнить foreach для массива с зазубринами, но 2D-массив - это не тот же тип структуры. Во всяком случае, ваш второй фрагмент не подходит для этой задачи, первый фрагмент не вложен.
Энтони Пеграм
4

Я знаю, что это старый пост, но я нашел его через Google, и, поиграв с ним, думаю, что у меня есть более простое решение. Если я ошибаюсь, укажите это, потому что я хотел бы знать, но это сработало, по крайней мере, для моих целей (это основано на ответе ICR):

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

Поскольку оба измерения ограничены, любое из них может быть простыми числами, что позволяет избежать вложенного цикла for. Признаюсь, я новичок в C #, поэтому, пожалуйста, если есть причина не делать этого, скажите мне ...

Кевен М
источник
1
Это предполагает длину 2-го измерения. Вы жестко кодируете предположение, что второе измерение всегда будет иметь длину 3, что может быть, а может и нет. Если это не 3, а, скажем, 50, вы столкнетесь с большим количеством копий / вставок, что сделает ваш код нечитаемым. :)
Адам Лир
@AnnaLear Предположение не обязательно неверно, возможно, вы обрабатываете матрицу четко определенной длины, такую ​​как доска для скрэббла 15x15,
Джейси
3

Двумерный массив в C # плохо подходит для вложенного foreach, он не эквивалент зубчатого массива (массива массивов). Вы можете сделать что-то вроде этого, чтобы использовать 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]);

Но вы все равно будете использовать i и j в качестве значений индекса для массива. Читаемость была бы лучше, если бы forвместо этого вы просто пошли по кругу .

Энтони Пеграм
источник
1
Это ужасная мутация классической, простой и понятной конструкции. Если ваш код содержит это, вам действительно нужно подумать: «Эй, это работает, но ... как насчет этого?» (Я прекрасно понимаю, что это идиоматика в семействе питонов. Однако - это C #.)
Rubys,
@Rubys, в Python это тоже не идиоматика. Получать индексы, а затем обращаться к списку с помощью []. Идиоматический Python - перебирать значения напрямую. Также обратите внимание, что в Python нет многомерных массивов или списков (только зубчатые).
Мэтью Флашен,
@Matthew: Я, возможно, перепутал питон с другим языком, я действительно не знаю свои руки от ног в семействе сценариев (что я имел в виду в семействе питонов, я полагаю)
Rubys
2

Два пути:

  1. Определите массив как зубчатый массив и используйте вложенные foreach.
  2. Определите массив как обычно и используйте foreach для всего.

Пример №2:

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

На выходе будет 1234. т.е. точно так же, как выполнение i от 0 до n и j от 0 до n.

Рубины
источник
2

Вы можете использовать такой метод расширения:

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

А потом:

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

Это будет немного медленнее, чем использование циклов for, но, вероятно, в большинстве случаев это не проблема. Не уверен, что это делает вещи более читаемыми.

Обратите внимание, что массивы C # могут быть отличными от нуля, поэтому вы можете использовать цикл for следующим образом:

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();
}
Йохан Ларссон
источник
странно ... что не с нуля?
drzaus 08
Я думаю, что при взаимодействии с Excel я видел ненулевое значение, это не является обычным явлением, но, возможно, полезно знать.
Йохан Ларссон,
1

Как упоминалось в другом месте, вы можете просто перебирать массив, и он выдаст все результаты по порядку во всех измерениях. Однако, если вы также хотите узнать индексы, то как насчет этого - http://blogs.msdn.com/b/ericlippert/archive/2010/06/28/computing-a-cartesian-product-with- linq.aspx

затем выполните что-то вроде:

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()));
}
Дэниел Смит
источник
1

Используйте LINQ .Cast<int>()для преобразования 2D-массива в IEnumerable<int>.

Пример LINQPad:

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

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

Вывод:

Последовательность 1,2,3,4,5,6

Angularsen
источник
0

Вы также можете использовать счетчики. Каждый тип массива любого измерения поддерживает метод Array.GetEnumerator. Единственный нюанс - с боксом / распаковкой придется разобраться. Однако код, который вам нужно написать, будет довольно тривиальным.

Вот пример кода:

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
источник
Голосовать -1 ... Using foreach is recommended, instead of directly manipulating the enumerator.
Иван
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
Сохель Ахмед
источник
0

Я искал решение для перечисления массива неизвестных во время компиляции ранга с доступом к каждому набору индексов элементов. Я видел решения с yield, но вот еще одна реализация без yield. Это в стиле олдскульного минимализма. В этом примере AppendArrayDebug () просто печатает все элементы в буфер 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;
    }
}

Например, следующий массив выдаст следующий результат:

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
источник