web-dev-qa-db-ja.com

多次元配列をどのようにループしますか?

foreach (String s in arrayOfMessages)
{
    System.Console.WriteLine(s);
}

string[,] arrayOfMessagesがパラメーターとして渡されています。

どの文字列がarrayOfMessages[0,i]arrayOfMessages[n,i]からのものであるかを判別できるようにしたいのですが、nは配列の最終インデックスです。

22
orange

ネストされた2つのforループを使用するだけです。次元のサイズを取得するには、 GetLength() を使用できます。

_for (int i = 0; i < arrayOfMessages.GetLength(0); i++)
{
    for (int j = 0; j < arrayOfMessages.GetLength(1); j++)
    {
        string s = arrayOfMessages[i, j];
        Console.WriteLine(s);
    }
}
_

これは、実際に_string[,]_があることを前提としています。 .Netでは、0からインデックス付けされていない多次元配列を使用することもできます。その場合、C#ではArrayとして表す必要があり、GetLowerBound()を使用する必要があります。 GetUpperBound()各次元の境界を取得します。

44
svick

ネストされたforループの場合:

for (int row = 0; row < arrayOfMessages.GetLength(0); row++)
{
   for (int col = 0; col < arrayOfMessages.GetLength(1); col++)
   {
      string message = arrayOfMessages[row,col];
      // use the message
   }    
}
10
Henk Holterman

foreachを使用しないでください-配列の各次元に1つずつ、ネストされたforループを使用します。

GetLength メソッドを使用して、各次元の要素数を取得できます。

MSDNの 多次元配列(C#プログラミングガイド) を参照してください。

7
Oded

あなたの問題に適した答えを見つけたようですが、タイトルは多次元配列(私は2 またはそれ以上と読みます)を要求するため、これは私が検索したときに得た最初の検索結果ですそれ、私は私のソリューションを追加します:

public static class MultidimensionalArrayExtensions
{
    /// <summary>
    /// Projects each element of a sequence into a new form by incorporating the element's index.
    /// </summary>
    /// <typeparam name="T">The type of the elements of the array.</typeparam>
    /// <param name="array">A sequence of values to invoke the action on.</param>
    /// <param name="action">An action to apply to each source element; the second parameter of the function represents the index of the source element.</param>
    public static void ForEach<T>(this Array array, Action<T, int[]> action)
    {
        var dimensionSizes = Enumerable.Range(0, array.Rank).Select(i => array.GetLength(i)).ToArray();
        ArrayForEach(dimensionSizes, action, new int[] { }, array);
    }
    private static void ArrayForEach<T>(int[] dimensionSizes, Action<T, int[]> action, int[] externalCoordinates, Array masterArray)
    {
        if (dimensionSizes.Length == 1)
            for (int i = 0; i < dimensionSizes[0]; i++)
            {
                var globalCoordinates = externalCoordinates.Concat(new[] { i }).ToArray();
                var value = (T)masterArray.GetValue(globalCoordinates);
                action(value, globalCoordinates);
            }
        else
            for (int i = 0; i < dimensionSizes[0]; i++)
                ArrayForEach(dimensionSizes.Skip(1).ToArray(), action, externalCoordinates.Concat(new[] { i }).ToArray(), masterArray);
    }

    public static void PopulateArray<T>(this Array array, Func<int[], T> calculateElement)
    {
        array.ForEach<T>((element, indexArray) => array.SetValue(calculateElement(indexArray), indexArray));
    }
}

使用例:

var foo = new string[,] { { "a", "b" }, { "c", "d" } };
foo.ForEach<string>((value, coords) => Console.WriteLine("(" + String.Join(", ", coords) + $")={value}"));
// outputs:
// (0, 0)=a
// (0, 1)=b
// (1, 0)=c
// (1, 1)=d

// Gives a 10d array where each element equals the sum of its coordinates:
var bar = new int[4, 4, 4, 5, 6, 5, 4, 4, 4, 5];
bar.PopulateArray(coords => coords.Sum());

一般的な考え方は、ディメンション全体を再帰することです。関数が効率性の賞を受賞しないと確信していますが、これはラティスの1回限りのイニシャライザとして機能し、値とインデックスを公開する十分なForEachが付属しています。私が解決していない主な欠点は、配列からTを自動的に認識させることです。そのため、型の安全性に関しては注意が必要です。

2
dananski

このような何かがうまくいくでしょう:

int length0 = arrayOfMessages.GetUpperBound(0) + 1;
int length1 = arrayOfMessages.GetUpperBound(1) + 1;

for(int i=0; i<length1; i++) { string msg = arrayOfMessages[0, i]; ... }
for(int i=0; i<length1; i++) { string msg = arrayOfMessages[length0-1, i]; ... }
1
Igor ostrovsky

より機能的なアプローチは、LINQを使用することです。これは常にループよりも優れています。これにより、コードの保守性と可読性が向上します。以下のコードスニペットは、メソッドまたはFluent LINQ構文を使用したソリューションの1つを示しています。

string[,] arrayOfMessages = new string[3, 2] { { "Col1","I am message 1" }, { "Col2", "I am message 2" }, { "Col3", "I am message 3" } };
var result = arrayOfMessages.Cast<string>()
                            .Where((msg, index) => index % 2 > 0);

foreach (var msg in result)
{
    Console.WriteLine(msg);
}

LINQ拡張メソッドは、IEnumerable<T>インターフェイスを実装していないため、多次元配列では使用できません。そこにCast<T>が登場します。基本的には配列全体をIEnumerable<T>にキャストします。この例では、多次元配列をIEnumerable<string>にフラット化します。

{ "Col1", "I am message 1", "Col2", "I am message 2", "Col3", "I am message 3" }

OfType<T>の代わりにCast<T>を使用することもできます。それらの唯一の違いは、データ型が混在するコレクションの場合、OfType<T>はキャストできない値を無視しますが、Cast<T>はInValidCastExceptionをスローすることです。

次に、必要なことは、偶数のインデックスでも値を無視(または除外)するLINQ演算子を適用することです。したがって、FuncデリゲートがFunc<TSource, int, bool>タイプのWhere演算子のオーバーロードを使用します。ここで、TSourceはコレクション内の各アイテム、intはコレクション内のアイテムであり、boolは戻り値の型です。

上記のスニペットでは、各項目のインデックスを評価し、奇数の場合にのみtrueを返すラムダ式を使用しました。

0
r4k35h

以下のコードを使用して、多次元配列を実行できます。

foreach (String s in arrayOfMessages)
{
    System.Console.WriteLine("{0}",s);
}
0
user3428474