Ответ 1
Если вы определяете свой массив следующим образом:
string[][] table = new string[][] { new string[] { "aa", "aaa" }, new string[]{ "bb", "bbb" } };
Затем вы можете использовать для него цикл foreach.
У меня есть двумерный массив,
string[,] table = {
{ "aa", "aaa" },
{ "bb", "bbb" }
};
И я хотел бы foreach
через него, как это,
foreach (string[] row in table)
{
Console.WriteLine(row[0] + " " + row[1]);
}
Но я получаю сообщение об ошибке:
Невозможно преобразовать строку типа в строку []
Есть ли способ, которым я могу достичь того, что я хочу, т.е. выполнить итерацию по первому размеру массива с переменной итератора, возвращая мне одномерный массив для этой строки?
Если вы определяете свой массив следующим образом:
string[][] table = new string[][] { new string[] { "aa", "aaa" }, new string[]{ "bb", "bbb" } };
Затем вы можете использовать для него цикл foreach.
Многомерные массивы не перечислимы. Просто повторите добрый старомодный способ:
for (int i = 0; i < table.GetLength(0); i++)
{
Console.WriteLine(table[i, 0] + " " + table[i, 1]);
}
Как и другие, вы можете использовать вложенные for-loops или redeclare ваш многомерный массив как зубчатый.
Однако, я думаю, стоит отметить, что многомерные массивы перечислимы, просто не так, как вы хотите. Например:
string[,] table = {
{ "aa", "aaa" },
{ "bb", "bbb" }
};
foreach (string s in table)
{
Console.WriteLine(s);
}
/* Output is:
aa
aaa
bb
bbb
*/
ОБНОВЛЕНИЕ. У меня было какое-то время на моих руках, поэтому... Я пошел вперед и сформулировал эту идею. Ниже приведен код.
Вот немного сумасшедшего ответа:
Вы можете делать то, что ищете, - по существу, рассматриваете двумерный массив как таблицу со строками - путем написания статического метода (возможно, метода расширения), который принимает T[,]
и возвращает IEnumerable<T[]>
. Это потребует копирования каждой "строки" базовой таблицы в новый массив.
Возможно, лучший (хотя и более привлекательный) подход заключается в том, чтобы на самом деле написать класс, который реализует IList<T>
как оболочку вокруг одной "строки" двумерного массива (вы, вероятно, установили бы значение IsReadOnly
в true и просто реализуйте getter для свойства this[int]
и, возможно, Count
и GetEnumerator
; все остальное может выбросить NotSupportedException
). Затем ваш статический/метод расширения может вернуть 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 перерыв на оставшуюся часть дня;)
Это зависит от того, как вы определяете свой многомерный массив. Вот два варианта:
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();
}
}
}
string[][] table = { ... };
Используя 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 вместо индекса 1. Если вы не знаете размерности вашего массива, тогда вы должны использовать GetLength() для определения размера и использования этого значения в частном.
Вот простой метод расширения, который возвращает каждую строку как 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) + "}");
}
Помните, что многомерный массив похож на таблицу. У вас нет элемента x и элемента y для каждой записи; у вас есть строка (например) table[1,2]
.
Итак, каждая запись по-прежнему остается только одной строкой (в вашем примере), это просто запись с определенным значением x/y. Итак, чтобы получить обе записи в table[1, x]
, вы сделали бы вложенный цикл. Что-то вроде следующего (не проверено, но должно быть близко)
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]);
}
}
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);
}
}
Я не большой поклонник этого метода из-за использования памяти, но если вы используете массивы, которые он производит, это не такая трата.
public static void ForEachRow<T>(this T[,] list, Action<int, T[]> action)
{
var len = list.GetLength(0);
var sub = list.GetLength(1);
T[] e;
int i, j;
for (i = 0; i < len; i++)
{
e = new T[sub];
for (j = 0; j < sub; j++)
{
e[j] = list[i, j];
}
action(i, e);
}
}
Реализация:
var list = new[,]{0x0, 0x1, 0x2, 0x4, 0x8};
list.ForEachRow((i, row) =>
{
for (var j = 0; j < row.Length; j++)
{
Console.WriteLine("[{0},{1}]: {2}", i, j, row[j]);
}
});
Другое найденное решение менее интенсивно для памяти, но будет использовать больше CPU, особенно когда размеры записей массивов больше.
public static void ForEachRow<T>(this T[,] list, Action<int, IEnumerable<T>> action)
{
var len = list.GetLength(0);
var sub = list.GetLength(1);
int i, j;
IEnumerable<T> e;
for (i = 0; i < len; i++)
{
e = Enumerable.Empty<T>();
for (j = 0; j < sub; j++)
{
e = e.Concat(AsEnumerable(list[i, j]));
}
action(i, e);
}
}
private static IEnumerable<T> AsEnumerable<T>(T add)
{
yield return add;
}
Реализация:
var list = new[,]{0x0, 0x1, 0x2, 0x4, 0x8};
list.ForEachRow((i, row) =>
{
var j = 0;
forrach (var o in row)
{
Console.WriteLine("[{0},{1}]: {2}", i, j, o);
++j;
}
});
В целом, я считаю первый вариант более интуитивным, особенно если вы хотите получить доступ к созданному массиву его индексом.
В конце концов, это всего лишь глазная конфета, и ни один из методов не должен использоваться для прямого доступа к исходному массиву;
for (var i = 0; i < list.GetLength(0); i++)
{
foreach (var j = 0; j < list.GetLength(1); j++)
{
Console.WriteLine("[{0},{1}]: {2}", i, j, list[i, j]);
}
}
Я пробую это. Я надеюсь помочь. Он работает с
static void Main()
{
string[,] matrix = {
{ "aa", "aaa" },
{ "bb", "bbb" }
};
int index = 0;
foreach (string element in matrix)
{
if (index < matrix.GetLength(1))
{
Console.Write(element);
if (index < (matrix.GetLength(1) - 1))
{
Console.Write(" ");
}
index++;
}
if (index == matrix.GetLength(1))
{
Console.Write("\n");
index = 0;
}
}