Ітерація багатовимірного масиву за допомогою вкладеного оператора 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]);
    }
}

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
Jaycee

3

2D-масив у 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, це теж не ідіоматично в Python. Дуже непитонічно отримувати індекси, а потім отримувати доступ до списку за допомогою []. Ідіоматичним Python є прямий перегляд значень. Також зауважте, що в Python немає багатовимірних масивів або списків (лише із зазубринами).
Метью Флашен

@Matthew: Можливо, я переплутав python з іншою мовою, я насправді не знаю своїх рук від ніг у сім'ї сценаріїв (що я мав на увазі в сім'ї python, я маю на увазі)
Рубіс,

2

Два шляхи:

  1. Визначте масив як зазубрений масив і використовуйте вкладені foreachs.
  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();
}

Це буде трохи повільніше, ніж використання для циклів, але, мабуть, це не проблема в більшості випадків. Не впевнений, чи робить це речі більш читабельними.

Зверніть увагу, що масиви 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

Здається, я бачив, що не взаємодіючи з 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


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

Голос -1 ... Цитую msdnUsing 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

Я шукав рішення для перерахування масиву невідомого при ранжуванні часу компіляції з доступом до кожного набору індексів елементів. Я бачив рішення з врожайністю, але ось ще одна реалізація без врожаю. Це по-старому мінімалістично. У цьому прикладі 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
*/
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.