2008-11-14 13 views
83

Similar a List<> OrderBy Alphabetical Order, queremos ordenar por un elemento, luego otro. Queremos lograr el equivalente funcional deC# List <> Ordena por x then y

SELECT * from Table ORDER BY x, y 

Tenemos una clase que contiene una serie de funciones de ordenación, y no tenemos problemas de clasificación en un elemento.
Por ejemplo:

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

List<MyClass> MyList; 

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

Y tenemos el siguiente en la 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 

ordenación estable sería preferible, pero no es obligatorio. La solución que funciona para .Net 2.0 es bienvenida.

+0

@Bolu he retirado explícitamente la etiqueta para hacer de correos versión respuestas agnósticos y actualizados para que coincida. Considere hacer una edición aclaratoria en la pregunta en lugar de restaurar la etiqueta si cree que 4.0/2.0 no fue lo suficientemente prominente. –

+0

Lo sentimos @AlexeiLevenkov, no presté mucha atención, no dude en retroceder. – Bolu

+0

OK. Revertido el cambio. –

Respuesta

97

Tenga en cuenta que no necesita una clasificación estable si compara todos los miembros. La solución 2.0, conforme a lo solicitado, puede tener este aspecto:

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

Ten en cuenta que esta solución 2.0 sigue siendo preferible a la solución popular 3.5 LINQ, se realiza una especie en el lugar y no tiene la O (n) requisito de almacenamiento del enfoque de Linq. A menos que prefiera que el objeto List original permanezca intacto, por supuesto.

150

Para las versiones de .Net donde se puede utilizar LINQ OrderBy y ThenBy (o ThenByDescending si es necesario):

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

Nota: para .NET 2.0 (o si no se puede utilizar LINQ) vea Hans Passant answer a esta pregunta.

+2

De otra respuesta publicada por phoog aquí: http://stackoverflow.com/questions/9285426/orderby-and-list-vs-iorderedenumerable Crea otra lista con los artículos originales en un nuevo pedido. Esto solo es útil si necesita preservar el orden original para algún otro propósito; es bastante más derrochador de memoria que ordenar la lista en su lugar – dreamerkumar

+0

Prestar atención: ThenBy se evalúa incluso si no se usa ... –

5

El truco es implementar un tipo estable. He creado una clase de widgets que puede contener los datos de prueba:

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

He implementado IComparable, para que pueda ser inestable ordenadas según la list.sort().

Sin embargo, también implementé el método estático Comparar, que se puede pasar como un delegado a un método de búsqueda.

me prestaron este método para ordenar la inserción de 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; 
      } 
    } 

Se podría poner esto en la clase ayudantes ordenar que usted ha mencionado en su pregunta.

Ahora, para 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); 
     } 
    } 

Y SALIDAS:

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

Esto probablemente podría ser limpiado con algunos delegados anónimos, pero voy a dejar que depende de usted.

EDIT: Y NoBugz demuestra el poder de los métodos anónimos ...entonces, considere el mío más oldschool: P

+0

¡Guau, gracias Jonathan, más allá! –

1

Tuve un problema donde OrderBy y ThenBy no me dieron el resultado deseado (o simplemente no sabía cómo usarlos correctamente).

Fui con una lista. Solución de trabajo algo como esto.

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