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.
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 #.
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 là, 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];
}
}
}
Here est un article archivé de Jim Mischel chez InformIt qui traite le tri des tableaux multidimensionnels rectangulaires et en escalier.
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]));
}
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();
}
}
}
}
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/
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 #.
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);
}
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
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.
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])
.
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();
}
}