2011-09-06 15 views
8

Załóżmy, że posiada poszarpaną tablicęoperacji tablic z tablicy n-wymiarowej LINQ (C#)

int[][] a = { new[] { 1, 2, 3, 4 }, new[] { 5, 6, 7, 8 }, new[] { 9, 10, 11, 12 } }; 

aby uzyskać sumę drugiego rzędu i suma drugiej kolumny, może zostać napisana obie linie kodu odpowiednio:

int rowSum = a[1].Sum(); 
int colSum = a.Select(row => row[1]).Sum(); 

Ale jeśli mamy definicję tablicy 2-wymiarowej

int[,] a = { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 } }; 

wyżej cytowanym kod nie będzie działać ze względu na compiller błędów:

Error 1 Wrong number of indices inside []; expected 2 
Error 2 'int[*,*]' does not contain a definition for 'Select' and no extension method 'Select' accepting a first argument of type 'int[*,*]' could be found (are you missing a using directive or an assembly reference?) 

Więc pytanie: Jak korzystać z metod LINQ z tablicami n-wymiarowej, ale nie te, postrzępione? I gdzie jest metoda przekształcania prostokątnej tablicy w poszarpane?

P.S. Próbowałem znaleźć odpowiedź w dokumentacji, ale bez rezultatu.

+0

Wielu tablice wymiarowe nie są naprawdę (obszernie) obsługiwane w języku C# :-( – xanatos

Odpowiedz

15

LINQ to Objects jest oparty na IEnumerable<T> Interface, tj. Jednowymiarowej sekwencji wartości. Oznacza to, że nie miesza się on dobrze z n-wymiarowymi strukturami danych, takimi jak tablice nieelastyczne, chociaż jest to możliwe.

Można wygenerować jednowymiarowy ciąg liczb całkowitych, że indeks w n-wymiarowej tablicy:

int rowSum = Enumerable.Range(0, a.GetLength(1)).Sum(i => a[1, i]); 

int colSum = Enumerable.Range(0, a.GetLength(0)).Sum(i => a[i, 1]); 
+1

+1: Ciekawe podejście –

3

O swoje pytanie "Jak używać metody LINQ z tablicami n-wymiarowej":

You nie może używać większości metod LINQ z macierzą wymiarową, ponieważ taka macierz implementuje tylko IEnumerable, ale nie ma ona żadnego znaczenia, a większość metod rozszerzania LINQ jest metodami rozszerzenia dla IEnumerable<T>.

O innym pytaniu: Zobacz odpowiedź dtb.

3

Aby dodać do roztworu DTB, w bardziej ogólny sposób iteracji przez wszystkie elementy tablicy byłoby:

int[,] b = { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 } }; 

var flattenedArray = Enumerable.Range(0, b.GetLength(0)) 
        .SelectMany(i => Enumerable.Range(0, b.GetLength(1)) 
         .Select(j => new { Row = i, Col = j })); 

A teraz:

var rowSum2 = flattenedArray.Where(t => t.Row == 1).Sum(t => b[t.Row, t.Col]); 
var colSum2 = flattenedArray.Where(t => t.Col == 1).Sum(t => b[t.Row, t.Col]); 

Oczywiście jest to bardzo rozrzutny jak tworzymy krotki współrzędnych, nawet dla tych elementów, które skończymy odfiltrowywać za pomocą Where, ale jeśli nie wiesz wcześniej, jakie są kryteria wyboru, to jest to droga (lub nie - to wydaje się bardziej jak ćwiczenie niż coś, co chciałbyś robić w praktyce).

Mogę sobie również wyobrazić, jak można to rozszerzyć na tablice dowolnej rangi (nie tylko 2D), używając rekurencyjnej lambda i czegoś w rodzaju Tuple, ale to przechodzi na terytorium masochizmu.

1

Tablica 2D nie ma żadnych wbudowanych metod iteracji w rzędzie lub kolumnie. Utworzenie własnej metody nie jest jednak trudne. Zobacz tę klasę dla implementacji, która pobiera przelicznik dla wiersza i kolumny.

public static class LINQTo2DArray 
{ 
    public static IEnumerable<T> Row<T>(this T[,] Array, int Row) 
    { 
     for (int i = 0; i < Array.GetLength(1); i++) 
     { 
      yield return Array[Row, i]; 
     } 
    } 
    public static IEnumerable<T> Column<T>(this T[,] Array, int Column) 
    { 
     for (int i = 0; i < Array.GetLength(0); i++) 
     { 
      yield return Array[i, Column]; 
     } 
    } 
} 

Można także spłaszczyć tablicę używając a.Cast<int>() ale byś wtedy luźne Wszystkie informacje na temat kolumn/wierszy

-1

Najprostszym tylko LINQ podejście widzę zrobić te rodzaje działalności wierszy i kolumn na zasadzie dwuwymiarowa tablica jest zdefiniowanie następujących wyszukiwań:

var firstColumnSum = cols[0].Sum(); 
:

var cols = a 
    .OfType<int>() 
    .Select((x, n) => new { x, n, }) 
    .ToLookup(xn => xn.n % a.GetLength(1), xn => xn.x); 

var rows = a 
    .OfType<int>() 
    .Select((x, n) => new { x, n, }) 
    .ToLookup(xn => xn.n/a.GetLength(1), xn => xn.x); 

teraz po prostu można to zrobić

Co do n-wymiarowej, to po prostu staje się zbyt bolesne ... Przepraszam.

0

Prostszy sposób robi jak poniżej

var t = new List<Tuple<int, int>>(); 
int[][] a = t.Select(x => new int[]{ x.Item1, x.Item2}).ToArray(); 
Powiązane problemy