2008-10-24 13 views
12

Say mam prostokątną tablicę ciągów - nie postrzępione tablicyJaki jest najlepszy sposób wyodrębnienia jednowymiarowej tablicy z tablicy prostokątnej w języku C#?

string[,] strings = new string[8, 3]; 

Jaki jest najlepszy sposób, aby wyodrębnić jednowymiarową tablicę z tego (albo pojedynczego wiersza lub pojedynczej kolumny)? Mogę to zrobić oczywiście za pomocą pętli for, ale mam nadzieję, że .NET ma bardziej elegancki sposób wbudowania.

Punkty premiowe za przekształcenie wyodrębnionej tablicy ciągów w tablicę obiektów.

Odpowiedz

7

przypadku prostokątnej tablicy:

string[,] rectArray = new string[3,3] { 
    {"a", "b", "c"}, 
    {"d", "e", "f"}, 
    {"g", "h", "i"} }; 

var rectResult = rectArray.Cast<object>().ToArray(); 

I postrzępioną tablicy:

string[][] jaggedArray = { 
    new string[] {"a", "b", "c", "d"}, 
    new string[] {"e", "f"}, 
    new string[] {"g", "h", "i"} }; 

var jaggedResult = jaggedArray.SelectMany(s => s).Cast<object>().ToArray(); 
1

LINQ jest odpowiedzią

static object[] GetColumn(string[][] source, int col) { 
    return source.Iterate().Select(x => source[x.Index][col]).Cast<object>().ToArray(); 
} 
static object[] GetRow(string[][] source, int row) { 
    return source.Skip(row).First().Cast<object>().ToArray(); 
} 
public class Pair<T> { 
    public int Index; 
    public T Value; 
    public Pair(int i, T v) { 
     Index = i; 
     Value = v; 
    } 
} 
static IEnumerable<Pair<T>> Iterate<T>(this IEnumerable<T> source) { 
    int index = 0; 
    foreach (var cur in source) { 
     yield return new Pair<T>(index, cur); 
     index++; 
    } 
} 
+0

Nie sądzę, że robi to, co został poproszony o („albo pojedynczego wiersza lub pojedynczej kolumny”). –

+0

Czy LINQ nie używa wewnętrznej pętli? – Rohit

+0

Dzięki za wskazanie, że Jon. Przeczytałem dokładnie tę część odpowiedzi. – JaredPar

13

Można rzucić tablicy ciągów do tablicy obiektów trywialnie - idzie w drugą stronę nie działa. Faktyczna ekstrakcja ma jednak, aby użyć pętli for, o ile widzę: Array.Copy wymaga, aby ranga źródła i celu była taka sama, a Buffer.BlockCopy działa tylko dla tablic typu wartości. Wydaje się to dziwne, ale ...

Możesz użyć LINQ, aby dodać wiersz lub kolumnę w pojedynczej instrukcji, chociaż będzie to nieefektywne (ponieważ utworzy listę wewnętrznie, a następnie trzeba ją przekonwertować na array - jeśli zrobisz to sam, możesz wstępnie przydzielić tablicę do odpowiedniego rozmiaru i skopiować bezpośrednio).

Kopiowanie wiersza (rowNum jest wiersz do skopiowania):

object[] row = Enumerable.Range(0, rowLength) 
         .Select(colNum => (object) stringArray[rowNum, colNum]) 
         .ToArray(); 

Kopiowanie kolumny (colNum jest kolumna do skopiowania):

object[] column = Enumerable.Range(0, columnLength) 
          .Select(rowNum => (object) stringArray[rowNum, colNum]) 
          .ToArray(); 

nie jestem pewien, że jest to naprawdę lepsze/prostsze niż pętla foreach - szczególnie jeśli napiszesz metodę ExtractRow i metodę ExtractColumn i ponownie je wykorzystasz.

+0

Tylko upewniam się, że nie odkrywam koła. To szczególne zadanie z pewnością nie występuje w przyrodzie na tyle często, by zagwarantować wbudowaną funkcję. – MusiGenesis

+1

Nie wiem - Myślę, że są bardziej niejasne rzeczy, które obsługuje framework. Wciąż jestem nieco zaskoczony, że nie ma kopii wiersza (jak to powinno być możliwe do wykreślenia). –

4

Chciałbym wyjaśnić (biorąc pod uwagę przykład i pytanie).

postrzępione Tablica jest szereg tablic i jest uznana tak:

string[][] data = new string[3][]; 
data[0] = new string[] { "0,[0]", "0,[1]", "0,[2]" }; 
data[1] = new string[] { "1,[0]", "1,[1]", "1,[2]" ]; 
data[2] = new string[] { "2,[0]", "1,[1]", "1,[2]" }; 

kontra prostokątnego macierzy zdefiniowany jako pojedynczej tablicy, która posiada wiele aspektów:

string[,] data = new string[3,3]; 
data[0,0] = "0,0"; 
data[0,1] = "0,1"; 
data[0,2] = "0,2"; 
...etc 

Z tego powodu, poszarpana tablica to IQueryable/IEnumerab le ponieważ możesz iterować nad nim, aby otrzymać tablicę w każdej iteracji. Podczas gdy prostokątna tablica jest nie IQueryable/IEnumerable, ponieważ elementy są adresowane w pełnym wymiarze (0,0 0,1..etc), więc nie będziesz mieć możliwości używania Linq lub jakichkolwiek predefiniowanych funkcji stworzonych dla Array w tym przypadku.

Choć można iteracyjne nad tablicy raz (i osiągnąć to, co chcesz) tak:

/// INPUT: rowIndex, OUTPUT: An object[] of data for that row 
int colLength = stringArray.GetLength(1); 
object[] rowData = new object[colLength]; 
for (int col = 0; col < colLength; col++) { 
    rowData[col] = stringArray[rowIndex, col] as object; 
} 
return rowData; 

/// INPUT: colIndex, OUTPUT: An object[] of data for that column 
int rowLength = stringArray.GetLength(0); 
object[] colData = new object[rowLength]; 
for (int row = 0; r < rowLength; row++) { 
    colData[row] = stringArray[row, colIndex] as object; 
} 
return colData; 

nadzieję, że to pomaga :)

+0

To nie jest prawda. Jak podano [tutaj] (http://msdn.microsoft.com/en-us/library/9b9dty7d.aspx), typy macierzy pochodzą z typu podstawowego [Array] (http://msdn.microsoft.com/en -us/library/system.array.aspx), który implementuje IEnumerable. Możesz więc użyć niektórych metod Linq, ale nie wszystkich. Obsada () na przykład działa dobrze. –

1

Wiersze mogą być kopiowane łatwo za pomocą Array.Copy:

 int[][] arDouble = new int[2][]; 
     arDouble[0] = new int[2]; 
     arDouble[1] = new int[2]; 
     arDouble[0][0] = 1; 
     arDouble[0][1] = 2; 
     arDouble[1][0] = 3; 
     arDouble[1][1] = 4; 

     int[] arSingle = new int[arDouble[0].Length]; 

     Array.Copy(arDouble[0], arSingle, arDouble[0].Length); 

Spowoduje to skopiowanie pierwszego wiersza do pojedynczej tablicy wymiarów.

+0

To nie działa dla dwóch tablic odwzorowania, jak widać w powyższej odpowiedzi nJxtom. –

+0

Sądzę, że terminologia to "tablica prostokątna", a nie "dwuwymiarowa". –

+0

Edytowałem pytanie, aby użyć "prostokątny" zamiast "dwuwymiarowy". – MusiGenesis

0

i wykonane metodą przedłużeniem. Nie wiem o występie.

public static class ExtensionMethods 
{ 
    public static string[] get1Dim(this string[,] RectArr, int _1DimIndex , int _2DimIndex ) 
    { 
     string[] temp = new string[RectArr.GetLength(1)]; 

     if (_2DimIndex == -1) 
     { 
      for (int i = 0; i < RectArr.GetLength(1); i++) 
      { temp[i] = RectArr[_1DimIndex, i]; } 
     } 
     else 
     { 
      for (int i = 0; i < RectArr.GetLength(0); i++) 
      { temp[i] = RectArr[ i , _2DimIndex]; } 
     } 

     return temp; 
     } 
} 

Wykorzystanie

// we now have this funtionaliy RectArray[1, * ] 
//          -1 means ALL  
string[] _1stRow = RectArray.get1Dim(0, -1) ;  
string[] _2ndRow = RectArray.get1Dim(1, -1) ; 

string[] _1stCol = RectArray.get1Dim(-1, 0) ;  
string[] _2ndCol = RectArray.get1Dim(-1, 1) ;