¿Cuál es la mejor manera de extraer una matriz unidimensional de una matriz rectangular en C #?

StackOverflow https://stackoverflow.com/questions/232545

  •  04-07-2019
  •  | 
  •  

Pregunta

Supongamos que tengo una matriz de cadena rectangular, no una matriz irregular

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

¿Cuál es la mejor manera de extraer una matriz unidimensional de esto (ya sea una sola fila o una sola columna)? Puedo hacer esto con un bucle for, por supuesto, pero espero que .NET tenga una forma más elegante incorporada.

Puntos de bonificación por convertir la matriz de cadenas extraída en una matriz de objetos.

¿Fue útil?

Solución

Para una matriz rectangular:

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

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

Y para una matriz irregular:

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

Otros consejos

Puedes convertir una matriz de cadenas a una matriz de objetos de forma trivial, ya que ir a la inversa no funciona. La extracción real tiene para usar un bucle for, por lo que puedo ver: Array.Copy requiere que los rangos de origen y destino sean los mismos, y Buffer.BlockCopy solo funciona para matrices de tipo de valor. Aunque parece extraño ...

Puede usar LINQ para agregar una fila o columna en una sola declaración, aunque será ineficiente (ya que generará una lista internamente, luego tendrá que convertirla en una matriz; si lo hace usted mismo, puede preasigne la matriz al tamaño correcto y cópiela directamente).

Copiando una fila ( rowNum es la fila a copiar):

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

Copiar una columna ( colNum es la columna a copiar):

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

No estoy seguro de que esto sea realmente mejor / más simple que un bucle foreach, especialmente si escribes un método ExtractRow y un método ExtractColumn y los reutilizas .

Me gustaría aclarar (dado el ejemplo y lo que se pregunta).

Una matriz dentada es una matriz de matrices y se declara así:

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

Versus una matriz rectangular se define como una única matriz que contiene múltiples dimensiones:

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

Debido a esto, un matriz irregular es IQueryable / IEnumerable porque puede iterar sobre ella para recibir una matriz en cada iteración. Mientras que una matriz rectangular es no IQueryable / IEnumerable porque los elementos están direccionados en dimensión completa (0,0 0,1..etc), por lo que no tendrá la capacidad de usar Linq o ninguna de las funciones predefinidas creadas para Array en ese caso.

Aunque puedes iterar sobre la matriz una vez (y lograr lo que quieres) de esta manera:

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

Espero que esto ayude :)

LINQ es la respuesta

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

Las filas se pueden copiar fácilmente usando 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);

Esto copiará la primera fila en la matriz de Dimensión única.

hice el método de extensión. No sé sobre el rendimiento.

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

uso

// 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) ; 
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top