Elenco C # <> Ordina per x poi y


88

Simile a List <> OrderBy Alphabetical Order , vogliamo ordinare per un elemento, poi un altro. vogliamo ottenere l'equivalente funzionale di

SELECT * from Table ORDER BY x, y  

Abbiamo una classe che contiene una serie di funzioni di ordinamento e non abbiamo problemi a ordinare in base a un elemento.
Per esempio:

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

List<MyClass> MyList;

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

E abbiamo il seguente nell'elenco:

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

L'ordinamento stabile sarebbe preferibile, ma non richiesto. La soluzione che funziona per .Net 2.0 è la benvenuta.


@ Bolu Ho rimosso esplicitamente il tag per rendere la versione del post agnostica e le risposte aggiornate per abbinarle. Considera l'idea di apportare una modifica chiarificatrice alla domanda invece di ripristinare il tag se ritieni che 4.0 / 2.0 non fosse abbastanza prominente.
Alexei Levenkov

Scusa @AlexeiLevenkov, non ho prestato molta attenzione, non esitare a tornare indietro.
Bolu

OK. Annullato il cambiamento.
Alexei Levenkov

Questa domanda è stata aggiornata per coprire tutte le versioni di .Net dall'originale solo 2.0 - contiene diverse risposte alternative per diversi framework e requisiti - controlla tutto per vedere quale si adatta meglio alle tue esigenze.
Alexei Levenkov

Risposte:


98

Tieni presente che non hai bisogno di un ordinamento stabile se confronti tutti i membri. La soluzione 2.0, come richiesto, può assomigliare a questa:

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

Si noti che questa soluzione 2.0 è ancora preferibile rispetto alla popolare soluzione 3.5 Linq, esegue un ordinamento sul posto e non ha i requisiti di archiviazione O (n) dell'approccio Linq. A meno che non si preferisca che l'oggetto List originale non venga toccato, ovviamente.


156

Per le versioni di .Net in cui è possibile utilizzare LINQ OrderBye ThenBy(o ThenByDescendingse necessario):

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

Nota: per .Net 2.0 (o se non puoi usare LINQ) vedi la risposta di Hans Passant a questa domanda.


2
Da un altro post risposta da phoog qui: stackoverflow.com/questions/9285426/...~~V~~singular~~3rd Crea un altro elenco con gli elementi originali in un nuovo ordine. Ciò è utile solo se è necessario mantenere l'ordine originale per altri scopi; è piuttosto più dispendioso di memoria che ordinare l'elenco sul posto
dreamerkumar


5

Il trucco è implementare un ordinamento stabile. Ho creato una classe Widget che può contenere i tuoi dati di 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;
    }
}

Ho implementato IComparable, quindi può essere ordinato in modo instabile da List.Sort ().

Tuttavia, ho anche implementato il metodo statico Compare, che può essere passato come delegato a un metodo di ricerca.

Ho preso in prestito questo metodo di ordinamento per inserimento da 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;
            }
    }

Lo inseriresti nella classe degli assistenti di ordinamento che hai menzionato nella tua domanda.

Ora, per usarlo:

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

E produce:

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

Probabilmente questo potrebbe essere ripulito con alcuni delegati anonimi, ma lo lascio a te.

EDIT : E NoBugz dimostra il potere dei metodi anonimi ... quindi, considera il mio più vecchia scuola: P



1

Ho avuto un problema in cui OrderBy e ThenBy non mi davano il risultato desiderato (o semplicemente non sapevo come usarli correttamente).

Sono andato con una lista.Sort soluzione qualcosa del genere.

    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)));
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.