web-dev-qa-db-fra.com

Comment trier un tableau à deux dimensions en C #?

J'ai un tableau à deux dimensions (de chaînes) qui composent ma table de données (de lignes et de colonnes). Je veux trier ce tableau par n'importe quelle colonne. J'ai essayé de trouver un algorithme pour le faire en C #, mais je n'ai pas réussi.

Toute aide est appréciée.

31
Jack

Chargez votre tableau de chaînes à deux dimensions dans un DataTable réel (System.Data.DataTable), puis utilisez la méthode Select () de l'objet DataTable pour générer un tableau trié d'objets DataRow (ou utilisez un DataView pour obtenir un effet similaire).

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

Vous pouvez également écrire votre propre méthode pour trier un tableau à deux dimensions. Les deux approches seraient des expériences d'apprentissage utiles, mais l'approche DataTable vous aiderait à apprendre à mieux gérer les tableaux de données dans une application C #.

23
MusiGenesis

Puis-je vérifier - voulez-vous dire un tableau rectangulaire ([,]) ou un tableau irrégulier ([][])?

Il est assez facile de trier un tableau en dents de scie; J'ai une discussion à ce sujet ici . Évidemment dans ce cas, le Comparison<T> impliquerait une colonne au lieu de trier par ordinal - mais très similaire.

Trier un tableau rectangulaire est plus délicat ... Je serais probablement tenté de copier les données dans un tableau rectangulaire ou dans un List<T[]> et de trier , puis de le copier.

Voici un exemple utilisant un tableau dentelé:

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

Pour travailler avec un tableau rectangulaire ... eh bien, voici un code à permuter entre les deux à la volée ...

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];
        }
    }
}
37
Marc Gravell

Here est un article archivé de Jim Mischel chez InformIt qui traite le tri des tableaux multidimensionnels rectangulaires et en escalier. 

6
Doug L.

Ce code devrait faire ce que vous voulez, je ne l'ai pas généralisé pour n par n, mais c'est simple. Cela dit - je suis d’accord avec MusiGenesis, en utilisant un autre objet un peu mieux adapté à cela (surtout si vous avez l’intention de faire une sorte de reliure)

(J'ai trouvé le code ici )

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]));
}
1
David Hall
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();
            }
        }
    }
}
1
Gregory Massov

On peut également consulter la méthode Array.Sort http://msdn.Microsoft.com/en-us/library/aa311213(v=vs.71).aspx

par exemple. Array.Sort (array, delegate (objet [] x, objet [] y)) {retour (x [i] comme IComparable) .CompareTo (y [i]);}); 

de http://channel9.msdn.com/forums/Coffeehouse/189171-Sorting-Two-Dimensional-Arrays-in-C/

1
No_Nick777

Donc, votre tableau est structuré comme ceci (je vais parler en pseudocode car mon C # -fu est faible, mais j'espère que vous obtiendrez l'essentiel de ce que je dis)

string values[rows][columns]

Donc value[1][3] est la valeur à la ligne 1, colonne 3.

Vous voulez trier par colonne, le problème est que votre tableau est désactivé à 90 degrés.

Pour commencer, pourriez-vous simplement le faire pivoter?

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 vous savez que vous ne voulez trier qu'une colonne à la fois, vous pouvez l'optimiser beaucoup en extrayant simplement les données que vous souhaitez trier:

  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++, vous pouvez jouer à des astuces sur la façon de calculer les décalages dans le tableau (puisque vous pourriez traiter votre tableau à deux dimensions comme un tableau à une dimension), mais je ne sais pas comment faire cela en c #.

0
Moishe Lettvin

Essayez ceci. La stratégie de base consiste à trier la colonne particulière indépendamment et à ne pas oublier la ligne d'origine de l'entrée. Le reste du code parcourt les données de colonne triées et remplace les lignes du tableau. La partie la plus délicate est de se rappeler de mettre à jour la colonne d'origine car la partie swap modifiera effectivement la colonne d'origine. 


        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
JaredPar

Je sais qu'il est tard, mais voici ma pensée que vous pourriez envisager.

par exemple c'est un tableau

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

et vous voulez le convertir par le numéro de colonne 2, puis 

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

Félicitations, vous avez trié le tableau par colonne souhaitée. Vous pouvez éditer ceci pour le faire fonctionner avec d'autres types de données

0
Mamoon Ahmed

Si vous pouviez obtenir les données sous forme de Tuple générique lorsque vous les lisiez ou les récupériez, cela serait beaucoup plus facile. il vous suffira alors d'écrire une fonction de tri comparant la colonne souhaitée du tuple, et vous disposerez d'un tableau de tuples à une seule dimension.

0
Raindog

C'est une vieille question, mais voici une classe que je viens de construire et basée sur l'article de Jim Mischel chez InformIt lié par 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]);
    }
}

Avec un tableau 2D non trié data de taille arbitraire que vous souhaitez trier sur la colonne 5, procédez comme suit:

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

Notez la méthode virtuelle Compare et protected SortArray afin de pouvoir créer des sous-classes spécialisées qui effectuent toujours un tri sur une colonne particulière ou un tri spécialisé sur plusieurs colonnes ou tout ce que vous souhaitez faire. C'est aussi pourquoi CompareStrings est éclaté et protégé - toutes les sous-classes peuvent l'utiliser pour des comparaisons simples au lieu de saisir la syntaxe complète SortArray[x, col].CompareTo(SortArray[y, col]).

0
Chuck Wilbur

J'aime l'approche DataTable proposée par MusiGenesis ci-dessus. La bonne chose à ce sujet est que vous pouvez trier par n’importe quelle chaîne SQL valide «commande par» qui utilise des noms de colonne, par exemple. "x, y desc, z" pour "commander par x, y desc, z". (FWIW, je ne pouvais pas le faire fonctionner en utilisant des ordinaux de colonnes, par exemple "3,2,1" pour "commander par 3,2,1"). le trier de n'importe quelle manière.

Dans l'exemple ci-dessous, j'ai d'abord chargé des données entières non triées dans un fichier tblToBeSorted in Sandbox (non illustré). Avec la table et ses données déjà existantes, je la charge (non triée) dans un tableau entier 2D, puis dans un DataTable. Le tableau de DataRows est la version triée de DataTable. L'exemple est un peu étrange dans la mesure où je charge mon tableau à partir de la base de données et que je l'aurais trié à ce moment-là, mais je voulais simplement obtenir un tableau non trié en C # à utiliser avec l'objet 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();
    }
}
0