2008-10-24 27 views
30

Tengo una matriz bidimensional (de cadenas) que compone mi tabla de datos (de filas y columnas). Quiero ordenar esta matriz por cualquier columna. Traté de encontrar un algoritmo para hacer esto en C#, pero no he tenido éxito.¿Cómo puedo ordenar una matriz bidimensional en C#?

Cualquier ayuda es apreciada.

Respuesta

20

Cargue su matriz de cadenas bidimensional en una DataTable real (System.Data.DataTable), y luego use el método Select() del objeto DataTable para generar una matriz ordenada de objetos DataRow (o use una DataView para obtener un efecto similar)

// assumes stringdata[row, col] is your 2D string array 
DataTable dt = new DataTable(); 
// assumes first row contains column names: 
for (int col = 0; col < stringdata.GetLength(1); col++) 
{ 
    dt.Columns.Add(stringdata[0, col]); 
} 
// load data from string array to data table: 
for (rowindex = 1; rowindex < stringdata.GetLength(0); rowindex++) 
{ 
    DataRow row = dt.NewRow(); 
    for (int col = 0; col < stringdata.GetLength(1); col++) 
    { 
     row[col] = stringdata[rowindex, col]; 
    } 
    dt.Rows.Add(row); 
} 
// sort by third column: 
DataRow[] sortedrows = dt.Select("", "3"); 
// sort by column name, descending: 
sortedrows = dt.Select("", "COLUMN3 DESC"); 

También puede escribir su propio método para ordenar una matriz bidimensional. Ambos enfoques serían experiencias de aprendizaje útiles, pero el enfoque de DataTable te ayudaría a aprender una forma mejor de manejar tablas de datos en una aplicación de C#.

+0

Eso suena interesante, puede publicar o enlazar algunos ejemplos de código, por favor. – Jack

+0

Hecho. Puede tener un error en alguna parte, lo escribí en el bloc de notas. – MusiGenesis

+0

Sorprendido, usted escribió eso en el bloc de notas, en cualquier caso, funcionó muy bien. Gracias. – Jack

6

Here es un artículo archivado de Jim Mischel en InformIt que maneja la ordenación para matrices multidimensionales rectangulares y dentadas.

+0

Ese ejemplo en realidad no ordena la matriz; el LINQ producirá una secuencia ordenada, pero solo si captura el resultado ... no ordena la matriz existente. Esto podría ser simplemente: string [] names = {"Smith", "Snyder", "Baker", "Jonson", "Ballmer"}; Array.Sort (nombres); –

+0

Veo lo que dice: eliminaré el ejemplo defectuoso, pero lo dejo en el enlace del artículo de clasificación. PD: gracias por decirme el motivo del voto a la baja. ¡No lo ves a menudo, pero realmente ES constructivo! –

+1

Y eliminé el voto atrasado desde que lo arregló ;-p –

0

Así que la matriz se estructura como esta (Voy a hablar en pseudocódigo porque mi C# -fu es débil, pero espero que obtiene la esencia de lo que estoy diciendo)

string values[rows][columns] 

Así value[1][3] es el valor en la fila 1, columna 3.

Desea ordenar por columna, por lo que el problema es que la matriz está desactivada en 90 grados.

Como primer corte, ¿podría simplemente rotarlo?

std::string values_by_column[columns][rows]; 

for (int i = 0; i < rows; i++) 
    for (int j = 0; j < columns; j++) 
    values_by_column[column][row] = values[row][column] 

sort_array(values_by_column[column]) 

for (int i = 0; i < rows; i++) 
    for (int j = 0; j < columns; j++) 
    values[row][column] = values_by_column[column][row] 

Si sabes que sólo desea ordenar una columna a la vez, se puede optimizar mucho esto con sólo extraer los datos que desea ordenar:

string values_to_sort[rows] 
    for (int i = 0; i < rows; i++) 
    values_to_sort[i] = values[i][column_to_sort] 

    sort_array(values_to_sort) 

    for (int i = 0; i < rows; i++) 
    values[i][column_to_sort] = values_to_sort[i] 

En C++ se puede jugar trucos con cómo calcular las compensaciones en la matriz (ya que podría tratar su matriz bidimensional como una matriz unificada) pero no estoy seguro de cómo hacer eso en C#.

1

Este código debe hacer lo que está buscando, no he generalizado para n por n, pero eso es sencillo. Dicho esto - Estoy de acuerdo con MusiGenesis, utilizando otro objeto que es un poco mejor se adapte a este (especialmente si usted tiene la intención de hacer ningún tipo de encuadernación)

(He encontrado el código here)

string[][] array = new string[3][]; 

array[0] = new string[3] { "apple", "apple", "apple" }; 
array[1] = new string[3] { "banana", "banana", "dog" }; 
array[2] = new string[3] { "cat", "hippo", "cat" };   

for (int i = 0; i < 3; i++) 
{ 
    Console.WriteLine(String.Format("{0} {1} {2}", array[i][0], array[i][1], array[i][2])); 
} 

int j = 2; 

Array.Sort(array, delegate(object[] x, object[] y) 
    { 
    return (x[j] as IComparable).CompareTo(y[ j ]); 
    } 
); 

for (int i = 0; i < 3; i++) 
{ 
    Console.WriteLine(String.Format("{0} {1} {2}", array[i][0], array[i][1], array[i][2])); 
} 
35

Puede Compruebo: ¿te refieres a una matriz rectangular ([,]) o una matriz dentada ([][])?

Es bastante fácil ordenar una matriz dentada; Tengo una discusión sobre eso here. Obviamente, en este caso, el Comparison<T> implicaría una columna en lugar de ordenar por ordinal, pero muy similar.

Ordenar una matriz rectangular es más complicado ... probablemente estaría tentado a copiar los datos a cabo ya sea en una matriz rectangular o una List<T[]>, y ordenar a allí, a continuación, copiar de nuevo.

He aquí un ejemplo utilizando una matriz escalonada:

static void Main() 
{ // could just as easily be string... 
    int[][] data = new int[][] { 
     new int[] {1,2,3}, 
     new int[] {2,3,4}, 
     new int[] {2,4,1} 
    }; 
    Sort<int>(data, 2); 
} 
private static void Sort<T>(T[][] data, int col) 
{ 
    Comparer<T> comparer = Comparer<T>.Default; 
    Array.Sort<T[]>(data, (x,y) => comparer.Compare(x[col],y[col])); 
} 

Para trabajar con una matriz rectangular ... bueno, he aquí algo de código para cambiar entre los dos sobre la marcha ...

static T[][] ToJagged<T>(this T[,] array) { 
    int height = array.GetLength(0), width = array.GetLength(1); 
    T[][] jagged = new T[height][]; 

    for (int i = 0; i < height; i++) 
    { 
     T[] row = new T[width]; 
     for (int j = 0; j < width; j++) 
     { 
      row[j] = array[i, j]; 
     } 
     jagged[i] = row; 
    } 
    return jagged; 
} 
static T[,] ToRectangular<T>(this T[][] array) 
{ 
    int height = array.Length, width = array[0].Length; 
    T[,] rect = new T[height, width]; 
    for (int i = 0; i < height; i++) 
    { 
     T[] row = array[i]; 
     for (int j = 0; j < width; j++) 
     { 
      rect[i, j] = row[j]; 
     } 
    } 
    return rect; 
} 
// fill an existing rectangular array from a jagged array 
static void WriteRows<T>(this T[,] array, params T[][] rows) 
{ 
    for (int i = 0; i < rows.Length; i++) 
    { 
     T[] row = rows[i]; 
     for (int j = 0; j < row.Length; j++) 
     { 
      array[i, j] = row[j]; 
     } 
    } 
} 
+0

Es rectangular. – Jack

+1

OK; He agregado algunos trucos ;-p –

+0

Esto es realmente más agradable ... –

0

Pruebe esto. La estrategia básica es ordenar la columna en particular de forma independiente y recordar la fila original de la entrada. El resto del código recorrerá los datos de la columna ordenada y cambiará las filas de la matriz. La parte difícil es recordar actualizar la columna original ya que la parte de intercambio alterará efectivamente la columna original.


     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++; 
      } 
     } 
     static void Sort2d(string[][] source, IComparer comp, int col) { 
      var colValues = source.Iterate() 
       .Select(x => new Pair<string>(x.Index,source[x.Index][col])).ToList(); 
      colValues.Sort((l,r) => comp.Compare(l.Value, r.Value)); 
      var temp = new string[source[0].Length]; 
      var rest = colValues.Iterate(); 
      while (rest.Any()) { 
       var pair = rest.First(); 
       var cur = pair.Value; 
       var i = pair.Index; 
       if (i == cur.Index) { 
        rest = rest.Skip(1); 
        continue; 
       } 

       Array.Copy(source[i], temp, temp.Length); 
       Array.Copy(source[cur.Index], source[i], temp.Length); 
       Array.Copy(temp, source[cur.Index], temp.Length); 
       rest = rest.Skip(1); 
       rest.Where(x => x.Value.Index == i).First().Value.Index = cur.Index; 
      } 
     } 

     public static void Test1() { 
      var source = new string[][] 
      { 
       new string[]{ "foo", "bar", "4" }, 
       new string[] { "jack", "dog", "1" }, 
       new string[]{ "boy", "ball", "2" }, 
       new string[]{ "yellow", "green", "3" } 
      }; 
      Sort2d(source, StringComparer.Ordinal, 2); 
     } 
0

Si se pudiera obtener los datos como una tupla genérico cuando lo lea en o lo recuperó, sería mucho más fácil; entonces solo tendría que escribir una función de ordenamiento que compare la columna deseada de la tupla, y usted tiene una matriz de tuplas de una sola dimensión.

0

Me gusta el enfoque DataTable propuesto por MusiGenesis arriba. Lo bueno de esto es que puedes ordenar por cualquier cadena SQL 'ordenar por' válida que use nombres de columnas, p. "x, y desc, z" para "ordenar por x, y desc, z". (FWIW, no pude hacer que funcionara usando ordinales de columna, por ejemplo, "3,2,1" para "ordenar por 3,2,1"). Solo usé números enteros, pero claramente se podían agregar datos de tipo mixto en DataTable y ordenarlo de cualquier manera.

En el ejemplo siguiente, primero cargué algunos datos enteros sin clasificar en un tblToBeSorted en Sandbox (no se muestra). Con la tabla y sus datos ya existentes, los cargo (sin clasificar) en una matriz de enteros 2D, luego en una DataTable. La matriz de DataRows es la versión ordenada de DataTable. El ejemplo es un poco extraño porque cargué mi matriz de la base de datos y pude haberlo ordenado en ese momento, pero solo quería obtener una matriz sin clasificar en C# para usar con el objeto DataTable.

static void Main(string[] args) 
{ 
    SqlConnection cnnX = new SqlConnection("Data Source=r90jroughgarden\\;Initial Catalog=Sandbox;Integrated Security=True"); 
    SqlCommand cmdX = new SqlCommand("select * from tblToBeSorted", cnnX); 
    cmdX.CommandType = CommandType.Text; 
    SqlDataReader rdrX = null; 
    if (cnnX.State == ConnectionState.Closed) cnnX.Open(); 

    int[,] aintSortingArray = new int[100, 4];  //i, elementid, planid, timeid 

    try 
    { 
     //Load unsorted table data from DB to array 
     rdrX = cmdX.ExecuteReader(); 
     if (!rdrX.HasRows) return; 

     int i = -1; 
     while (rdrX.Read() && i < 100) 
     { 
      i++; 
      aintSortingArray[i, 0] = rdrX.GetInt32(0); 
      aintSortingArray[i, 1] = rdrX.GetInt32(1); 
      aintSortingArray[i, 2] = rdrX.GetInt32(2); 
      aintSortingArray[i, 3] = rdrX.GetInt32(3); 
     } 
     rdrX.Close(); 

     DataTable dtblX = new DataTable(); 
     dtblX.Columns.Add("ChangeID"); 
     dtblX.Columns.Add("ElementID"); 
     dtblX.Columns.Add("PlanID"); 
     dtblX.Columns.Add("TimeID"); 
     for (int j = 0; j < i; j++) 
     { 
      DataRow drowX = dtblX.NewRow(); 
      for (int k = 0; k < 4; k++) 
      { 
       drowX[k] = aintSortingArray[j, k]; 
      } 
      dtblX.Rows.Add(drowX); 
     } 

     DataRow[] adrowX = dtblX.Select("", "ElementID, PlanID, TimeID"); 
     adrowX = dtblX.Select("", "ElementID desc, PlanID asc, TimeID desc"); 

    } 
    catch (Exception ex) 
    { 
     string strErrMsg = ex.Message; 
    } 
    finally 
    { 
     if (cnnX.State == ConnectionState.Open) cnnX.Close(); 
    } 
} 
1
using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 

namespace ConsoleApplication1 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      int[,] arr = { { 20, 9, 11 }, { 30, 5, 6 } }; 
      Console.WriteLine("before"); 
      for (int i = 0; i < arr.GetLength(0); i++) 
      { 
       for (int j = 0; j < arr.GetLength(1); j++) 
       { 
        Console.Write("{0,3}", arr[i, j]); 
       } 
       Console.WriteLine(); 
      } 
      Console.WriteLine("After"); 

      for (int i = 0; i < arr.GetLength(0); i++) // Array Sorting 
      { 
       for (int j = arr.GetLength(1) - 1; j > 0; j--) 
       { 

        for (int k = 0; k < j; k++) 
        { 
         if (arr[i, k] > arr[i, k + 1]) 
         { 
          int temp = arr[i, k]; 
          arr[i, k] = arr[i, k + 1]; 
          arr[i, k + 1] = temp; 
         } 
        } 
       } 
       Console.WriteLine(); 
      } 

      for (int i = 0; i < arr.GetLength(0); i++) 
      { 
       for (int j = 0; j < arr.GetLength(1); j++) 
       { 
        Console.Write("{0,3}", arr[i, j]); 
       } 
       Console.WriteLine(); 
      } 
     } 
    } 
} 
0

Ésta es una vieja pregunta, pero aquí es una clase acabo construido en base a the article from Jim Mischel at InformIt unidos por Doug L.

class Array2DSort : IComparer<int> 
{ 
    // maintain a reference to the 2-dimensional array being sorted 
    string[,] _sortArray; 
    int[] _tagArray; 
    int _sortIndex; 

    protected string[,] SortArray { get { return _sortArray; } } 

    // constructor initializes the sortArray reference 
    public Array2DSort(string[,] theArray, int sortIndex) 
    { 
     _sortArray = theArray; 
     _tagArray = new int[_sortArray.GetLength(0)]; 
     for (int i = 0; i < _sortArray.GetLength(0); ++i) _tagArray[i] = i; 
     _sortIndex = sortIndex; 
    } 

    public string[,] ToSortedArray() 
    { 
     Array.Sort(_tagArray, this); 
     string[,] result = new string[ 
      _sortArray.GetLength(0), _sortArray.GetLength(1)]; 
     for (int i = 0; i < _sortArray.GetLength(0); i++) 
     { 
      for (int j = 0; j < _sortArray.GetLength(1); j++) 
      { 
       result[i, j] = _sortArray[_tagArray[i], j]; 
      } 
     } 
     return result; 
    } 

    // x and y are integer row numbers into the sortArray 
    public virtual int Compare(int x, int y) 
    { 
     if (_sortIndex < 0) return 0; 
     return CompareStrings(x, y, _sortIndex); 
    } 

    protected int CompareStrings(int x, int y, int col) 
    { 
     return _sortArray[x, col].CompareTo(_sortArray[y, col]); 
    } 
} 

Dada una matriz 2D sin clasificar data de tamaño arbitrario que desea ordenar en la columna 5, simplemente haga esto:

 Array2DSort comparer = new Array2DSort(data, 5); 
     string[,] sortedData = comparer.ToSortedArray(); 

Tenga en cuenta el virtual Compare método y protegido SortArray para que pueda crear subclases especializadas que siempre ordenan en una columna particular o hacen una clasificación especializada en columnas múltiples o lo que sea que desee hacer. Es por eso que CompareStrings está roto y protegido: cualquier subclase puede usarlo para comparaciones simples en lugar de escribir la sintaxis completa de SortArray[x, col].CompareTo(SortArray[y, col]).

0

Sé que es tarde pero aquí está mi pensamiento que tal vez desee considerar.

por ejemplo, esto es gama

{ 
m,m,m 
a,a,a 
b,b,b 
j,j,j 
k,l,m 
} 

y desea convertirlo en número de Coloumn 2, entonces

string[] newArr = new string[arr.length] 
for(int a=0;a<arr.length;a++) 
newArr[a] = arr[a][1] + a; 
// create new array that contains index number at the end and also the coloumn values 
Array.Sort(newArr); 
for(int a=0;a<newArr.length;a++) 
{ 
int index = Convert.ToInt32(newArr[a][newArr[a].Length -1]); 
//swap whole row with tow at current index 
if(index != a) 
{ 
string[] arr2 = arr[a]; 
arr[a] = arr[index]; 
arr[index] = arr2; 
} 
} 

Felicidades has ordenados por la matriz de Coloumn deseada. Puede editar esto para que funcione con otros tipos de datos

Cuestiones relacionadas