web-dev-qa-db-fra.com

Liste C # <> Trier par x puis y

Similaire à List <> OrderBy par ordre alphabétique , nous voulons trier par élément, puis par autre. nous voulons atteindre 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

Un type stable serait préférable, mais pas obligatoire. Une solution qui fonctionne pour .Net 2.0 est la bienvenue.

86
Byron Ross

Gardez à l'esprit que vous n'avez pas besoin d'une sorte stable si vous comparez tous les membres. La solution 2.0, à la demande, peut ressembler à ceci:

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

Notez que cette solution 2.0 est toujours préférable à la solution populaire Linq 3.5, elle effectue un tri sur place et ne requiert pas le stockage O(n)) de l’approche Linq. préférez que l'objet List d'origine ne soit pas touché, bien sûr.

98
Hans Passant

Pour les versions de .Net où vous pouvez utiliser LINQ OrderBy et ThenBy (ou ThenByDescending si nécessaire):

using System.Linq;
....
List<SomeClass>() a;
List<SomeClass> b = a.OrderBy(x => x.x).ThenBy(x => x.y).ToList();

Remarque: pour .Net 2.0 (ou si vous ne pouvez pas utiliser LINQ), voir réponse de Hans Passant à cette question.

154
Toby

Vous devez implémenter l'interface IComparer . Voici un bon article avec un exemple de code.

7
Bill the Lizard

L'astuce consiste à implémenter une sorte stable. J'ai créé une classe de widget pouvant 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, de sorte qu'il peut être trié de manière instable par List.Sort ().

Cependant, j'ai également implémenté la méthode statique Compare, qui peut être transmise en tant que 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 placeriez cela dans la classe des aides de 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 cela donne:

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

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

[~ # ~] edit [~ # ~] : Et NoBugz démontre le pouvoir des méthodes anonymes ... alors considérez le mien plus oldschool: P

5
FlySwat
4
Bala

J'ai eu un problème où OrderBy et ThenBy ne m'ont pas donné le résultat souhaité (ou je ne savais tout simplement pas comment les utiliser correctement).

Je suis allé avec une liste. Solution de tri quelque chose comme ça.

    var data = (from o in database.Orders Where o.ClientId.Equals(clientId) select new {
    OrderId = o.id,
    OrderDate = o.orderDate,
    OrderBoolean = (SomeClass.SomeFunction(o.orderBoolean) ? 1 : 0)
    });

    data.Sort((o1, o2) => (o2.OrderBoolean.CompareTo(o1.OrderBoolean) != 0
    o2.OrderBoolean.CompareTo(o1.OrderBoolean) : o1.OrderDate.Value.CompareTo(o2.OrderDate.Value)));
1
mofoo