Как я 'foreach' через двумерный массив? - PullRequest
43 голосов
/ 14 мая 2010

У меня есть двумерный массив,

string[,] table = {
                       { "aa", "aaa" },
                       { "bb", "bbb" }
                   };

И я бы хотел foreach пройти через это,

foreach (string[] row in table)
{
    Console.WriteLine(row[0] + " " + row[1]);
}

Но я получаю ошибку:

Невозможно преобразовать строку типа в строку []

Есть ли способ, которым я могу достичь того, чего хочу, то есть перебрать первое измерение массива с помощью переменной итератора, возвращающей мне одномерный массив для этой строки?

Ответы [ 12 ]

57 голосов
/ 14 мая 2010

Многомерные массивы не перечисляются. Просто повторяем старый добрый путь:

for (int i = 0; i < table.GetLength(0); i++)
{
    Console.WriteLine(table[i, 0] + " " + table[i, 1]);
}
24 голосов
/ 14 мая 2010

Если вы определите свой массив следующим образом:

string[][] table = new string[][] { new string[] { "aa", "aaa" }, new string[]{ "bb", "bbb" } };

Тогда вы можете использовать цикл foreach для него.

23 голосов
/ 14 мая 2010

Как и предполагали другие, вы можете использовать вложенные циклы for или повторно объявить ваш многомерный массив как зубчатый.

Тем не менее, я думаю, стоит отметить, что многомерные массивы перечисляются , но не так, как вы хотите. Например:

string[,] table = {
                      { "aa", "aaa" },
                      { "bb", "bbb" }
                  };

foreach (string s in table)
{
    Console.WriteLine(s);
}

/* Output is:
  aa
  aaa
  bb
  bbb
*/
15 голосов
/ 14 мая 2010

ОБНОВЛЕНИЕ : у меня было немного времени, так что ... я пошел дальше и развил эту идею. Ниже приведен код.


Вот немного сумасшедший ответ:

Вы могли бы делать то, что искали - по сути, обрабатывать двумерный массив как таблицу со строками - написав статический метод (возможно, метод расширения), который принимает T[,] и возвращает IEnumerable<T[]>. Однако для этого потребуется скопировать каждую «строку» базовой таблицы в новый массив.

Возможно, лучший (хотя и более сложный) подход заключается в написании класса, который реализует IList<T> в качестве оболочки вокруг одной "строки" двумерного массива (вы, вероятно, установите IsReadOnly в значение true и просто реализуйте метод получения для свойства this[int] и, возможно, Count и GetEnumerator; все остальное может выдать NotSupportedException). Тогда ваш метод static / extension может вернуть IEnumerable<IList<T>> и обеспечить отложенное выполнение.

Таким образом, вы могли бы написать код, очень похожий на то, что у вас есть:

foreach (IList<string> row in table.GetRows()) // or something
{
    Console.WriteLine(row[0] + " " + row[1]);
}

Просто мысль.


Предложение по реализации:

public static class ArrayTableHelper {
    public static IEnumerable<IList<T>> GetRows<T>(this T[,] table) {
        for (int i = 0; i < table.GetLength(0); ++i)
            yield return new ArrayTableRow<T>(table, i);
    }

    private class ArrayTableRow<T> : IList<T> {
        private readonly T[,] _table;
        private readonly int _count;
        private readonly int _rowIndex;

        public ArrayTableRow(T[,] table, int rowIndex) {
            if (table == null)
                throw new ArgumentNullException("table");

            if (rowIndex < 0 || rowIndex >= table.GetLength(0))
                throw new ArgumentOutOfRangeException("rowIndex");

            _table = table;
            _count = _table.GetLength(1);
            _rowIndex = rowIndex;
        }

        // I didn't implement the setter below,
        // but you easily COULD (and then set IsReadOnly to false?)
        public T this[int index] {
            get { return _table[_rowIndex, index]; }
            set { throw new NotImplementedException(); }
        }

        public int Count {
            get { return _count; }
        }

        bool ICollection<T>.IsReadOnly {
            get { return true; }
        }

        public IEnumerator<T> GetEnumerator() {
            for (int i = 0; i < _count; ++i)
                yield return this[i];
        }

        // omitted remaining IList<T> members for brevity;
        // you actually could implement IndexOf, Contains, etc.
        // quite easily, though
    }
}

... теперь я думаю, что я должен дать StackOverflow перерыв на остаток дня;)

4 голосов
/ 02 сентября 2013

Это зависит от того, как вы определяете свой многомерный массив. Вот два варианта:

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            // First
            string[,] arr1 = {
                       { "aa", "aaa" },
                       { "bb", "bbb" }
                   };

            // Second
            string[][] arr2 = new[] {
                new[] { "aa", "aaa" },
                new[] { "bb", "bbb" }
            };

            // Iterate through first
            for (int x = 0; x <= arr1.GetUpperBound(0); x++)
                for (int y = 0; y <= arr1.GetUpperBound(1); y++)
                    Console.Write(arr1[x, y] + "; ");

            Console.WriteLine(Environment.NewLine);

            // Iterate through second second
            foreach (string[] entry in arr2)
                foreach (string element in entry)
                    Console.Write(element + "; ");

            Console.WriteLine(Environment.NewLine);
            Console.WriteLine("Press any key to finish");
            Console.ReadKey();
        }
    }
}
3 голосов
/ 14 мая 2010
string[][] table = { ... };
2 голосов
/ 04 сентября 2017

Вот простой метод расширения, который возвращает каждую строку как IEnumerable<T>. Преимущество в том, что вы не используете дополнительную память:

public static class Array2dExt
{
    public static IEnumerable<IEnumerable<T>> Rows<T>(this T[,] array)
    {
        for (int r = array.GetLowerBound(0); r <= array.GetUpperBound(0); ++r)
            yield return row(array, r);
    }

    static IEnumerable<T> row<T>(T[,] array, int r)
    {
        for (int c = array.GetLowerBound(1); c <= array.GetUpperBound(1); ++c)
            yield return array[r, c];
    }
}

Пример использования:

static void Main()
{
    string[,] siblings = { { "Mike", "Amy" }, { "Mary", "Albert" }, {"Fred", "Harry"} };

    foreach (var row in siblings.Rows())
        Console.WriteLine("{" + string.Join(", ", row) + "}");
}
2 голосов
/ 02 сентября 2013

Используя LINQ, вы можете сделать это следующим образом:

var table_enum = table

    // Convert to IEnumerable<string>
    .OfType<string>()

    // Create anonymous type where Index1 and Index2
    // reflect the indices of the 2-dim. array
    .Select((_string, _index) => new {
        Index1 = (_index / 2),
        Index2 = (_index % 2), // ← I added this only for completeness
        Value = _string
    })

    // Group by Index1, which generates IEnmurable<string> for all Index1 values
    .GroupBy(v => v.Index1)

    // Convert all Groups of anonymous type to String-Arrays
    .Select(group => group.Select(v => v.Value).ToArray());

// Now you can use the foreach-Loop as you planned
foreach(string[] str_arr in table_enum) {
    // …
}

Таким образом, можно также использовать foreach для циклического перемещения по столбцам вместо строк, используя Index2 в GroupBy вместо Index 1. Если вы не знаете размерности вашего массива, вам придется использовать Метод GetLength () для определения измерения и использования этого значения в частном.

1 голос
/ 16 января 2011
string[][] languages = new string[2][];
            languages[0] = new string[2];
            languages[1] = new string[3];
// inserting data into double dimensional arrays.
            for (int i = 0; i < 2; i++)
            {
                languages[0][i] = "Jagged"+i.ToString();
            }
            for (int j = 0; j < 3; j++)
            {
                languages[1][j] = "Jag"+j.ToString();
            }

// doing foreach through 2 dimensional arrays.
foreach (string[] s in languages)
            {
                foreach (string a in s)
                {
                    Console.WriteLine(a);
                }
            }
1 голос
/ 14 мая 2010

Помните, что многомерный массив похож на таблицу. У вас нет элемента x и элемента y для каждой записи; у вас есть строка (например) table[1,2].

Итак, каждая запись - это всего лишь одна строка (в вашем примере), это просто запись с определенным значением x / y. Таким образом, чтобы получить обе записи на table[1, x], вы должны выполнить вложенный цикл for. Что-то вроде следующего (не проверено, но должно быть близко)

for (int x = 0; x < table.Length; x++)
{
    for (int y = 0; y < table.Length; y += 2)
    {
        Console.WriteLine("{0} {1}", table[x, y], table[x, y + 1]);
    }
}
...