C # -Liste <> Nach x und dann nach y sortieren

88

Ähnlich wie bei List <> OrderBy Alphabetical Order möchten wir nach einem Element und dann nach einem anderen sortieren. wir wollen das funktionale Äquivalent von erreichen

SELECT * from Table ORDER BY x, y  

Wir haben eine Klasse, die eine Reihe von Sortierfunktionen enthält, und wir haben keine Probleme beim Sortieren nach einem Element.
Beispielsweise:

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

List<MyClass> MyList;

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

Und wir haben folgendes in der 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

Eine stabile Sortierung wäre vorzuziehen, aber nicht erforderlich. Eine Lösung, die für .Net 2.0 funktioniert, ist willkommen.

Byron Ross
quelle
@Bolu Ich habe das Tag explizit entfernt, um die Post-Version agnostisch zu machen, und die Antworten entsprechend aktualisiert. Nehmen Sie eine klarstellende Änderung in der Frage vor, anstatt das Tag wiederherzustellen, wenn Sie der Meinung sind, dass 4.0 / 2.0 nicht prominent genug war.
Alexei Levenkov
Sorry @AlexeiLevenkov, habe nicht viel Aufmerksamkeit geschenkt, bitte zögern Sie nicht, ein Rollback durchzuführen.
Bolu
OK. Die Änderung wurde rückgängig gemacht.
Alexei Levenkov
Diese Frage wurde aktualisiert, um alle Versionen von .Net aus dem Original nur 2.0 abzudecken - enthält mehrere alternative Antworten für verschiedene Frameworks und Anforderungen - überprüfen Sie alle, um herauszufinden, welche Ihren Anforderungen besser entspricht.
Alexei Levenkov

Antworten:

98

Denken Sie daran, dass Sie keine stabile Sortierung benötigen, wenn Sie alle Mitglieder vergleichen. Die angeforderte 2.0-Lösung kann folgendermaßen aussehen:

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

Beachten Sie, dass diese 2.0-Lösung der beliebten 3.5 Linq-Lösung immer noch vorzuziehen ist. Sie führt eine direkte Sortierung durch und erfüllt nicht die O (n) -Speicheranforderungen des Linq-Ansatzes. Es sei denn, Sie möchten natürlich, dass das ursprüngliche List-Objekt unberührt bleibt.

Hans Passant
quelle
156

Für Versionen von .Net, in denen Sie LINQ verwenden können OrderByund ThenBy(oder ( ThenByDescendingfalls erforderlich)):

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

Hinweis: Für .Net 2.0 (oder wenn Sie LINQ nicht verwenden können) siehe Hans Passant Antwort auf diese Frage.

Toby
quelle
2
Aus einem anderen Antwortbeitrag von phoog hier: stackoverflow.com/questions/9285426/… Es wird eine weitere Liste mit den Originalartikeln in einer neuen Reihenfolge erstellt. Dies ist nur nützlich, wenn Sie die ursprüngliche Bestellung für einen anderen Zweck beibehalten müssen. Es ist eher eine Verschwendung von Speicher als das Sortieren der Liste an Ort und Stelle
dreamerkumar
5

Der Trick besteht darin, eine stabile Sortierung zu implementieren. Ich habe eine Widget-Klasse erstellt, die Ihre Testdaten enthalten kann:

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

Ich habe IComparable implementiert, damit es instabil nach List.Sort () sortiert werden kann.

Ich habe jedoch auch die statische Methode Compare implementiert, die als Delegat an eine Suchmethode übergeben werden kann.

Ich habe diese Einfügesortiermethode von C # 411 ausgeliehen :

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

Sie würden dies in die Klasse Helfer Klasse setzen, die Sie in Ihrer Frage erwähnt haben.

Nun, um es zu benutzen:

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

Und es gibt aus:

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

Dies könnte wahrscheinlich mit einigen anonymen Delegierten aufgeräumt werden, aber das überlasse ich Ihnen.

EDIT : Und NoBugz demonstriert die Macht anonymer Methoden ... also betrachten Sie meine mehr oldschool: P.

FlySwat
quelle
1

Ich hatte ein Problem, bei dem OrderBy und ThenBy mir nicht das gewünschte Ergebnis lieferten (oder ich wusste einfach nicht, wie ich sie richtig verwenden sollte).

Ich ging mit einer Liste. Sortierlösung so etwas.

    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)));
mofoo
quelle