2008-11-14 8 views
83

Simile a List<> OrderBy Alphabetical Order, vogliamo ordinare per un elemento, quindi un altro. vogliamo raggiungere l'equivalente funzionale diC# Elenco <> Ordina per x poi y

SELECT * from Table ORDER BY x, y 

Abbiamo una classe che contiene una serie di funzioni di ordinamento, e non abbiamo problemi di ordinamento per un elemento.
Per esempio:

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

List<MyClass> MyList; 

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

E abbiamo la segue nella lista:

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 

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

+0

@Bolu ho esplicitamente rimosso il tag per rendere Versione post risposte agnostiche e aggiornati in modo che corrisponda. Considera di apportare una modifica chiarificatrice nella domanda invece di ripristinare il tag se pensi che 4.0/2.0 non sia abbastanza prominente. –

+0

Scusate @AlexeiLevenkov, non ha prestato molta attenzione, non esitate a roll-back. – Bolu

+0

OK. Reveduto il cambiamento. –

risposta

97

Tenere presente che non è necessario un ordinamento stabile se si confrontano tutti i membri. La soluzione 2,0, come richiesto, può apparire come questo:

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

Do atto che questa soluzione 2.0 è ancora preferibile rispetto della popolare soluzione di 3.5 LINQ, esegue una sorta sul posto e non hanno la O (n) requisito di archiviazione dell'approccio Linq. A meno che non si preferisca naturalmente l'oggetto List originale a essere intatto.

150

Per le versioni di Net dove è possibile utilizzare LINQ OrderBy e ThenBy (o ThenByDescending se 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 è possibile utilizzare LINQ) vedi Hans Passant answer a questa domanda.

+2

Da un altro post di risposta di phoog qui: http://stackoverflow.com/questions/9285426/orderby-and-list-vs-iorderedenumerable Crea un altro elenco con gli elementi originali in un nuovo ordine. Questo è utile solo se è necessario conservare l'ordinamento originale per qualche altro scopo; è piuttosto più dispendioso di memoria che ordinare l'elenco sul posto – dreamerkumar

+0

Attenzione: ThenBy viene valutato anche se non viene utilizzato ... –

5

Il trucco è implementare un ordinamento stabile. Ho creato una classe Widget che può contenere i 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, in modo che possa essere instabile allineati secondo list.sort().

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

ho preso in prestito questo metodo sorta 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; 
      } 
    } 

Si potrebbe mettere questo nella classe aiutanti di ordinamento che lei ha citato nella sua interrogazione.

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

Ed uscite:

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

Questo potrebbe probabilmente essere ripulito con alcuni delegati anonimi, ma lascio che a voi.

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

+0

Wow, grazie Jonathan, al di sopra e al di là! –

1

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

Sono andato con un elenco. Soluzione di questo tipo.

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