87 votes

C# List<> Trier par x puis y

Question similaire à celle-ci, mais en 2.0, nous voulons trier par un élément, puis par un autre. Nous voulons obtenir l'équivalent fonctionnel de SELECT * from Table ORDER BY x, y

Nous avons une classe qui contient un certain nombre de fonctions de tri, et nous n'avons aucun problème à trier par un élément. Par exemple :

public class MyClass {
    public int x;
    public int y;
}  

List<MyClass> MyList;

public void SortList() {
    MyList.Sort( MySortingFunction );
}

Et nous avons les éléments suivants dans la liste :

Unsorted     Sorted(x)     Desired
---------    ---------    ---------
ID   x  y    ID   x  y    ID   x  y
[0]  0  1    [2]  0  2    [0]  0  1
[1]  1  1    [0]  0  1    [2]  0  2
[2]  0  2    [1]  1  1    [1]  1  1
[3]  1  2    [3]  1  2    [3]  1  2

Je pense que nous cherchons un moyen d'implémenter un tri stable, mais je suis heureux de prendre toutes les suggestions !

éditer :

OK, comme cela semble souvent être le cas, le fait de poser la question a libéré un blocage mental !

@nobugz - J'ai implémenté ceci juste avant de voir votre réponse ! Merci, c'est la façon de faire dans ce cas. Sauf que je n'utilise pas de méthodes anonymes, car j'ai plusieurs sortes différentes pour cette classe.

@Jonathan Holland - Si je dois ajouter une autre sorte, j'utiliserai quelque chose comme le vôtre !

@Bill The Lizard - ce lien MS m'a rafraîchi la mémoire !

155voto

Toby Points 1823
List<SomeClass>() a;
List<SomeClass> b = a.OrderBy(x => x.x).ThenBy(x => x.y).ToList();
etc.

Voir également ThenByDescending ;

edit : sorry didn't notice the 2.0

98voto

Hans Passant Points 475940

Vous n'avez pas besoin d'un tri stable si vous comparez tous les membres. Par exemple :

public void SortList() {
   MyList.Sort(delegate(MyClass a, MyClass b)
   {
      int xdiff = a.x.CompareTo(b.x);
      if (xdiff != 0) return xdiff;
      else return a.y.CompareTo(b.y);
   });
}

7voto

Bill the Lizard Points 147311

Vous devez mettre en œuvre le IComparer interface. Voici un bon article avec un exemple de code.

5voto

FlySwat Points 61945

L'astuce consiste à mettre en œuvre un tri stable. J'ai créé une classe Widget qui peut contenir vos données de test :

public class Widget : IComparable
{
    int x;
    int y;
    public int X
    {
        get { return x; }
        set { x = value; }
    }

    public int Y
    {
        get { return y; }
        set { y = value; }
    }

    public Widget(int argx, int argy)
    {
        x = argx;
        y = argy;
    }

    public int CompareTo(object obj)
    {
        int result = 1;
        if (obj != null && obj is Widget)
        {
            Widget w = obj as Widget;
            result = this.X.CompareTo(w.X);
        }
        return result;
    }

    static public int Compare(Widget x, Widget y)
    {
        int result = 1;
        if (x != null && y != null)                
        {                
            result = x.CompareTo(y);
        }
        return result;
    }
}

J'ai implémenté IComparable, afin qu'il puisse être trié de manière instable par List.Sort().

Cependant, j'ai également implémenté la méthode statique Compare, qui peut être transmise comme délégué à une méthode de recherche.

J'ai emprunté cette méthode de tri par insertion à C# 411 :

 public static void InsertionSort<T>(IList<T> list, Comparison<T> comparison)
        {           
            int count = list.Count;
            for (int j = 1; j < count; j++)
            {
                T key = list[j];

                int i = j - 1;
                for (; i >= 0 && comparison(list[i], key) > 0; i--)
                {
                    list[i + 1] = list[i];
                }
                list[i + 1] = key;
            }
    }

Vous devez placer ces éléments dans la classe d'aide au tri que vous avez mentionnée dans votre question.

Maintenant, pour l'utiliser :

    static void Main(string[] args)
    {
        List<Widget> widgets = new List<Widget>();

        widgets.Add(new Widget(0, 1));
        widgets.Add(new Widget(1, 1));
        widgets.Add(new Widget(0, 2));
        widgets.Add(new Widget(1, 2));

        InsertionSort<Widget>(widgets, Widget.Compare);

        foreach (Widget w in widgets)
        {
            Console.WriteLine(w.X + ":" + w.Y);
        }
    }

Et ça donne des résultats :

0:1
0:2
1:1
1:2
Press any key to continue . . .

Cela pourrait probablement être nettoyé avec des délégués anonymes, mais je vous laisse le soin de le faire.

EDIT : Et NoBugz démontre la puissance des méthodes anonymes... donc, considérez le mien plus oldschool :P

3voto

Bala Points 89

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X