Lặp lại mảng đa chiều với câu lệnh báo trước lồng nhau


79

Tôi nghĩ đây có thể là một câu hỏi khá đơn giản, nhưng tôi vẫn chưa thể tìm ra nó. Nếu tôi có một mảng 2 chiều như vậy:

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

Cách tốt nhất để lặp qua từng thứ nguyên của mảng bằng câu lệnh foreach lồng nhau là gì?


Nó phải là một mảng hai chiều, hay bạn có thể sử dụng một mảng nhiều mảng?
Matthew Flaschen

Câu trả lời:


133

Nếu bạn muốn lặp lại mọi mục trong mảng như thể nó là một mảng phẳng, bạn chỉ cần thực hiện:

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

cái nào sẽ in

123456

Nếu bạn cũng muốn biết chỉ số x và y, bạn cần phải làm:

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

Ngoài ra, bạn có thể sử dụng một mảng răng cưa để thay thế (một mảng các mảng):

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

hoặc là

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
Sau khi thấy những sai lầm trong cách làm của tôi (ví dụ: cố gắng sử dụng một mảng 2D như một mảng răng cưa), tôi đồng ý rằng cách tốt nhất để lặp lại danh sách đó giống như bạn có trong ví dụ đầu tiên của mình. Bạn nhận được phiếu bầu vì bạn sẵn sàng vượt ra ngoài câu trả lời và đưa ra các lựa chọn khác.
Tyler Murry

Tại sao mảng 2 mờ bị phẳng?
Sipo

C # = nó luôn hoạt động. C ++ = sẽ không hoạt động trước khi bạn lãng phí thời gian.
jw_

21

Đây là cách truy cập từng phần tử trong mảng 2 chiều. Đây có phải là những gì bạn đang tìm kiếm?

for (int i=0;i<array.GetLength(0);i++)
{
    for (int j=0;j<array.GetLength(1);j++)
    {
        int cell = array[i,j];
    }
}

1
Tôi đã hy vọng triển khai foreach , nếu điều đó có thể?
Tyler Murry

6

Với mảng nhiều chiều, bạn có thể sử dụng cùng một phương pháp để lặp qua các phần tử, ví dụ:

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

Kết quả của ví dụ này là:

9 99 3 33 5 55

Người giới thiệu


Trong Java, mảng nhiều chiều là mảng của các mảng, vì vậy các hoạt động sau:

    int[][] table = {
            { 1, 2, 3 },
            { 4, 5, 6 },
    };
    for (int[] row : table) {
        for (int el : row) {
            System.out.println(el);
        }
    }

3
C # có mảng nhiều chiều và mảng răng cưa là các khái niệm riêng biệt, đâu int[,]là mảng 2 chiều và int[][]là mảng răng cưa của mảng và mỗi mảng nhất định không bắt buộc phải có cùng độ dài. Bạn có thể dễ dàng thực hiện bước trước trên mảng răng cưa, nhưng mảng 2D không phải là cùng một kiểu cấu trúc. Ở bất kỳ mức độ nào, đoạn mã thứ hai của bạn không phù hợp với vấn đề này , đoạn mã đầu tiên không được lồng vào nhau.
Anthony Pegram

4

Tôi biết đây là một bài viết cũ, nhưng tôi đã tìm thấy nó thông qua Google và sau khi chơi với nó, tôi nghĩ rằng tôi có một giải pháp dễ dàng hơn. Nếu tôi sai, vui lòng chỉ ra, 'vì tôi muốn biết, nhưng điều này ít nhất cũng phù hợp với mục đích của tôi (Nó dựa trên phản hồi của ICR):

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

Vì cả hai thứ nguyên đều bị giới hạn, một trong hai thứ nguyên có thể là số đơn giản và do đó tránh được vòng lặp for lồng nhau. Tôi thừa nhận rằng tôi là người mới sử dụng C #, vì vậy, xin vui lòng, nếu có lý do gì để không làm điều đó, vui lòng cho tôi biết ...


1
Đây là giả sử chiều dài của kích thước thứ 2. Bạn đang giả định rằng kích thước thứ hai sẽ luôn có độ dài 3, điều này có thể đúng hoặc không. Nếu không phải là 3 mà là 50, bạn sẽ phải xem rất nhiều bản sao / dán và làm cho mã của bạn không thể đọc được. :)
Adam Lear

@AnnaLear Giả định là không nhất thiết phải sai lầm, bạn có thể xử lý một ma trận của một chiều dài được xác định rõ như 15x15 gải board
Jaycee

3

Mảng 2D trong C # không phù hợp với một foreach lồng nhau, nó không tương đương với một mảng răng cưa (một mảng các mảng). Bạn có thể làm điều gì đó như thế này để sử dụng 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]);

Nhưng bạn vẫn sẽ sử dụng i và j làm giá trị chỉ mục cho mảng. Khả năng đọc sẽ được duy trì tốt hơn nếu forthay vào đó bạn chỉ sử dụng vòng lặp đa dạng trong vườn .


1
Đây là một đột biến khủng khiếp của một cấu trúc cổ điển, đơn giản và dễ hiểu. Nếu mã của bạn chứa cái này, bạn thực sự cần phải nghĩ "này, cái này hoạt động, nhưng ... không thì sao?" (Tôi biết rõ đây là câu thành ngữ trong họ trăn. Tuy nhiên - Đây là C #.)
Rubys

@Rubys, điều này cũng không phải là thành ngữ trong Python. Nó là rất đơn giản để lấy các chỉ mục sau đó truy cập danh sách với []. Nó là một Python thành ngữ để lặp qua các giá trị trực tiếp. Cũng lưu ý rằng không có mảng hoặc danh sách đa chiều trong Python (chỉ có răng cưa).
Matthew Flaschen

@Matthew: Tôi có thể đã nhầm lẫn python với một ngôn ngữ khác, tôi thực sự không biết cánh tay từ chân của mình trong họ kịch bản (đó là ý của tôi trong họ trăn, tôi cho là vậy)
Rubys

2

Hai lối:

  1. Định nghĩa mảng là một mảng răng cưa và sử dụng các foreachs lồng nhau.
  2. Xác định mảng bình thường và sử dụng foreach trên toàn bộ.

Ví dụ về # 2:

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

Đầu ra sẽ là 1234. tức là. hoàn toàn giống như thực hiện i từ 0 đến n và j từ 0 đến n.


2

Bạn có thể sử dụng một phương pháp mở rộng như sau:

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

Và sau đó:

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

Nó sẽ chậm hơn một chút so với sử dụng vòng lặp for nhưng có lẽ không phải là vấn đề trong hầu hết các trường hợp. Không chắc liệu nó có làm cho mọi thứ dễ đọc hơn không.

Lưu ý rằng mảng c # có thể khác với mảng dựa trên 0, vì vậy bạn có thể sử dụng vòng lặp for như sau:

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

kỳ lạ ... những gì không dựa trên số không?
drzaus

Tôi nghĩ rằng tôi đã thấy khác 0 dựa trên khi tương tác với Excel, nó không phổ biến nhưng có lẽ tốt để biết.
Johan Larsson

1

Như đã đề cập ở phần khác, bạn chỉ có thể lặp lại mảng và nó sẽ tạo ra tất cả các kết quả theo thứ tự trên tất cả các thứ nguyên. Tuy nhiên, nếu bạn cũng muốn biết các chỉ số, thì hãy sử dụng nó - http://blogs.msdn.com/b/ericlippert/archive/2010/06/28/computing-a-cartesian-product-with- linq.aspx

sau đó làm một cái gì đó như:

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

Sử dụng LINQ .Cast<int>()để chuyển đổi mảng 2D sang IEnumerable<int>.

Ví dụ về LINQPad:

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

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

Đầu ra:

Trình tự là 1,2,3,4,5,6


0

Bạn cũng có thể sử dụng điều tra viên. Mọi kiểu mảng của bất kỳ thứ nguyên nào đều hỗ trợ phương thức Array.GetEnumerator. Cảnh báo duy nhất là bạn sẽ phải đối phó với quyền anh / unboxing. Tuy nhiên, mã bạn cần viết sẽ khá tầm thường.

Đây là mã mẫu:

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

Bỏ phiếu -1 ... Tôi trích dẫn msdnUsing foreach is recommended, instead of directly manipulating the enumerator.
Ivan

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

Tôi đang tìm kiếm một giải pháp để liệt kê một mảng không xác định ở xếp hạng thời gian biên dịch với quyền truy cập vào mọi bộ chỉ số phần tử. Tôi đã thấy các giải pháp có năng suất nhưng đây là một cách triển khai khác không có năng suất. Đó là theo cách tối giản trường học cũ. Trong ví dụ này, AppendArrayDebug () chỉ in tất cả các phần tử vào bộ đệm 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;
    }
}

Ví dụ, mảng sau sẽ tạo ra kết quả sau:

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
*/
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.