it-swarm-es.tech

Cómo ordenar una lista <T> por una propiedad en el objeto

Tengo una clase llamada Order que tiene propiedades como OrderId, OrderDate, Quantity y Total. Tengo una lista de esta clase de Order:

List<Order> objListOrder = new List<Order>();
GetOrderList(objListOrder); // fill list of orders

Ahora quiero ordenar la lista en función de una propiedad del objeto Order, por ejemplo, necesito ordenarla por fecha de pedido o ID de pedido.

¿Cómo puedo hacer esto en C #?

1054
Shyju

La forma más fácil en que puedo pensar es usar Linq:

List<Order> SortedList = objListOrder.OrderBy(o=>o.OrderDate).ToList();
1537
Lazarus

Si necesita ordenar la lista en el lugar, puede usar el método SortNAME_ , pasando un Comparison<T> delegado:

objListOrder.Sort((x, y) => x.OrderDate.CompareTo(y.OrderDate));

Si prefiere crear una nueva secuencia ordenada en lugar de una ordenación local, puede utilizar el método OrderByNAME_ LIN de LINQ, como se menciona en las otras respuestas.

629
LukeH

Para hacer esto sin LINQ en .Net2.0:

List<Order> objListOrder = GetOrderList();
objListOrder.Sort(
    delegate(Order p1, Order p2)
    {
        return p1.OrderDate.CompareTo(p2.OrderDate);
    }
);

Si estás en .Net3.0, entonces lo que buscas es answer de LukeH.

Para ordenar en múltiples propiedades, todavía puede hacerlo dentro de un delegado. Por ejemplo:

orderList.Sort(
    delegate(Order p1, Order p2)
    {
        int compareDate = p1.Date.CompareTo(p2.Date);
        if (compareDate == 0)
        {
            return p2.OrderID.CompareTo(p1.OrderID);
        }
        return compareDate;
    }
);

Esto le daría ascendente fechas con descendente orderIds.

Sin embargo, no recomendaría pegar delegados, ya que significará muchos lugares sin la reutilización del código. Debe implementar una IComparer y simplemente transmitirla a su método Sort. Ver aquí .

public class MyOrderingClass : IComparer<Order>
{
    public int Compare(Order x, Order y)
    {
        int compareDate = x.Date.CompareTo(y.Date);
        if (compareDate == 0)
        {
            return x.OrderID.CompareTo(y.OrderID);
        }
        return compareDate;
    }
}

Y luego, para usar esta clase de IComparer, simplemente cree una instancia y pásela a su método de clasificación:

IComparer<Order> comparer = new MyOrderingClass();
orderList.Sort(comparer);
206
djdd87

La forma más sencilla de ordenar una lista es usar OrderBy

 List<Order> objListOrder = 
    source.OrderBy(order => order.OrderDate).ToList();

Si desea ordenar por varias columnas como sigue la consulta SQL.

ORDER BY OrderDate, OrderId

Para lograr esto puedes usar ThenBy como sigue.

  List<Order> objListOrder = 
    source.OrderBy(order => order.OrderDate).ThenBy(order => order.OrderId).ToList();
92
PSK

Haciéndolo sin Linq como dijiste:

public class Order : IComparable
{
    public DateTime OrderDate { get; set; }
    public int OrderId { get; set; }

    public int CompareTo(object obj)
    {
        Order orderToCompare = obj as Order;
        if (orderToCompare.OrderDate < OrderDate || orderToCompare.OrderId < OrderId)
        {
            return 1;
        }
        if (orderToCompare.OrderDate > OrderDate || orderToCompare.OrderId > OrderId)
        {
            return -1;
        }

        // The orders are equivalent.
        return 0;
    }
}

Luego solo llame a .sort () en su lista de Pedidos

32
Jimmy Hoffa

Una solución orientada a objetos clásicos

Primero debo hacer una genuflexión a la genialidad de LINQ ... Ahora que ya lo hemos dejado de lado

Una variación en la respuesta de JimmyHoffa. Con los genéricos, el parámetro CompareTo se vuelve de tipo seguro.

public class Order : IComparable<Order> {

    public int CompareTo( Order that ) {
        if ( that == null ) return 1;
        if ( this.OrderDate > that.OrderDate) return 1;
        if ( this.OrderDate < that.OrderDate) return -1;
        return 0;
    }
}

// in the client code
// assume myOrders is a populated List<Order>
myOrders.Sort(); 

Esta clasificación por defecto es reutilizable, por supuesto. Es decir, cada cliente no tiene que volver a escribir de forma redundante la lógica de clasificación. Cambiar el "1" y el "-1" (o los operadores lógicos, su elección) invierte el orden de clasificación.

23
radarbob

// Clasificación totalmente genérica para usar con una vista de cuadrícula

public List<T> Sort_List<T>(string sortDirection, string sortExpression, List<T> data)
    {

        List<T> data_sorted = new List<T>();

        if (sortDirection == "Ascending")
        {
            data_sorted = (from n in data
                              orderby GetDynamicSortProperty(n, sortExpression) ascending
                              select n).ToList();
        }
        else if (sortDirection == "Descending")
        {
            data_sorted = (from n in data
                              orderby GetDynamicSortProperty(n, sortExpression) descending
                              select n).ToList();

        }

        return data_sorted;

    }

    public object GetDynamicSortProperty(object item, string propName)
    {
        //Use reflection to get order type
        return item.GetType().GetProperty(propName).GetValue(item, null);
    }
18
roger

Aquí hay un método de extensión LINQ genérico que no crea una copia adicional de la lista:

public static void Sort<T,U>(this List<T> list, Func<T, U> expression)
    where U : IComparable<U>
{
    list.Sort((x, y) => expression.Invoke(x).CompareTo(expression.Invoke(y)));
}

Para usarlo:

myList.Sort(x=> x.myProperty);

Recientemente construí este adicional que acepta un ICompare<U>, para que pueda personalizar la comparación. Esto fue útil cuando necesité hacer un ordenamiento natural de cuerdas:

public static void Sort<T, U>(this List<T> list, Func<T, U> expression, IComparer<U> comparer)
    where U : IComparable<U>
{    
    list.Sort((x, y) => comparer.Compare(expression.Invoke(x), expression.Invoke(y)));
}
5
Peter

Usando LINQ

objListOrder = GetOrderList()
                   .OrderBy(o => o.OrderDate)
                   .ToList();

objListOrder = GetOrderList()
                   .OrderBy(o => o.OrderId)
                   .ToList();
4
Daniel A. White
//Get data from database, then sort list by staff name:

List<StaffMember> staffList = staffHandler.GetStaffMembers();

var sortedList = from staffmember in staffList
                 orderby staffmember.Name ascending
                 select staffmember;
3
Waqas Ahmed

Una versión mejorada de Roger.

El problema con GetDynamicSortProperty es que solo se obtienen los nombres de las propiedades, pero ¿qué sucede si en GridView usamos NavigationProperties? Enviará una excepción, ya que encuentra nula.

Ejemplo:

"Employee.Company.Name;" se bloqueará ... ya que solo permite "Nombre" como parámetro para obtener su valor.

Aquí hay una versión mejorada que nos permite ordenar por propiedades de navegación.

public object GetDynamicSortProperty(object item, string propName)
    {
        try
        {                 
            string[] prop = propName.Split('.'); 

            //Use reflection to get order type                   
            int i = 0;                    
            while (i < prop.Count())
            {
                item = item.GetType().GetProperty(prop[i]).GetValue(item, null);
                i++;
            }                     

            return item;
        }
        catch (Exception ex)
        {
            throw ex;
        }


    } 
3
user1013375

Puede hacer algo más genérico sobre la selección de propiedades, pero ser específico sobre el tipo de selección, en su caso 'Orden':

escribe tu función como una genérica:

public List<Order> GetOrderList<T>(IEnumerable<Order> orders, Func<Order, T> propertySelector)
        {
            return (from order in orders
                    orderby propertySelector(order)
                    select order).ToList();
        } 

y luego usarlo de esta manera:

var ordersOrderedByDate = GetOrderList(orders, x => x.OrderDate);

Puede ser aún más genérico y definir un tipo abierto para lo que desea ordenar:

public List<T> OrderBy<T,P>(IEnumerable<T> collection, Func<T,P> propertySelector)
        {
            return (from item in collection
                    orderby propertySelector(item)
                    select item).ToList();
        } 

y utilízalo de la misma manera:

var ordersOrderedByDate = OrderBy(orders, x => x.OrderDate);

Esta es una forma compleja e innecesaria de hacer un estilo LINQ 'OrderBy', pero puede darle una idea de cómo puede implementarse de una manera genérica

3
Danny Mor

Permítame completar la respuesta de @LukeH con un código de ejemplo, ya que la he probado, creo que puede ser útil para algunos:

public class Order
{
    public string OrderId { get; set; }
    public DateTime OrderDate { get; set; }
    public int Quantity { get; set; }
    public int Total { get; set; }

    public Order(string orderId, DateTime orderDate, int quantity, int total)
    {
        OrderId = orderId;
        OrderDate = orderDate;
        Quantity = quantity;
        Total = total;
    }
}

public void SampleDataAndTest()
{
    List<Order> objListOrder = new List<Order>();

    objListOrder.Add(new Order("tu me paulo ", Convert.ToDateTime("01/06/2016"), 1, 44));
    objListOrder.Add(new Order("ante laudabas", Convert.ToDateTime("02/05/2016"), 2, 55));
    objListOrder.Add(new Order("ad ordinem ", Convert.ToDateTime("03/04/2016"), 5, 66));
    objListOrder.Add(new Order("collocationem ", Convert.ToDateTime("04/03/2016"), 9, 77));
    objListOrder.Add(new Order("que rerum ac ", Convert.ToDateTime("05/02/2016"), 10, 65));
    objListOrder.Add(new Order("locorum ; cuius", Convert.ToDateTime("06/01/2016"), 1, 343));


    Console.WriteLine("Sort the list by date ascending:");
    objListOrder.Sort((x, y) => x.OrderDate.CompareTo(y.OrderDate));

    foreach (Order o in objListOrder)
        Console.WriteLine("OrderId = " + o.OrderId + " OrderDate = " + o.OrderDate.ToString() + " Quantity = " + o.Quantity + " Total = " + o.Total);

    Console.WriteLine("Sort the list by date descending:");
    objListOrder.Sort((x, y) => y.OrderDate.CompareTo(x.OrderDate));
    foreach (Order o in objListOrder)
        Console.WriteLine("OrderId = " + o.OrderId + " OrderDate = " + o.OrderDate.ToString() + " Quantity = " + o.Quantity + " Total = " + o.Total);

    Console.WriteLine("Sort the list by OrderId ascending:");
    objListOrder.Sort((x, y) => x.OrderId.CompareTo(y.OrderId));
    foreach (Order o in objListOrder)
        Console.WriteLine("OrderId = " + o.OrderId + " OrderDate = " + o.OrderDate.ToString() + " Quantity = " + o.Quantity + " Total = " + o.Total);

    //etc ...
}
3
molbalga
var obj = db.Items.Where...

var orderBYItemId = obj.OrderByDescending(c => Convert.ToInt32(c.ID));
2
Jevgenij Kononov

Ninguna de las respuestas anteriores fue lo suficientemente genérica para mí, así que hice esta:

var someUserInputStringValue = "propertyNameOfObject i.e. 'Quantity' or 'Date'";
var SortedData = DataToBeSorted
                   .OrderBy(m => m.GetType()
                                  .GetProperties()
                                  .First(n => 
                                      n.Name == someUserInputStringValue)
                   .GetValue(m, null))
                 .ToList();

Cuidado con los conjuntos de datos masivos sin embargo. Es un código fácil, pero podría causarle problemas si la colección es enorme y el tipo de objeto de la colección tiene una gran cantidad de campos. El tiempo de ejecución es NxM donde:

N = # de elementos en la colección

M = # de propiedades dentro del objeto

1
itcropper

Cualquiera que trabaje con tipos anulables, se requiere Value para usar CompareTo.

objListOrder.Sort((x, y) => x.YourNullableType.Value.CompareTo(y.YourNullableType.Value));

1
Jude

Hacer uso de LiNQ OrderBy

List<Order> objListOrder=new List<Order> ();
    objListOrder=GetOrderList().OrderBy(o=>o.orderid).ToList();
1
Pranay Rana

Basado en GenericTypeTea 'Comparer:
podemos obtener más flexibilidad agregando indicadores de clasificación:

public class MyOrderingClass : IComparer<Order> {  
    public int Compare(Order x, Order y) {  
        int compareDate = x.Date.CompareTo(y.Date);  
        if (compareDate == 0) {  
            int compareOrderId = x.OrderID.CompareTo(y.OrderID);  

            if (OrderIdDescending) {  
                compareOrderId = -compareOrderId;  
            }  
            return compareOrderId;  
        }  

        if (DateDescending) {  
            compareDate = -compareDate;  
        }  
        return compareDate;  
    }  

    public bool DateDescending { get; set; }  
    public bool OrderIdDescending { get; set; }  
}  

En este escenario, debe crear una instancia como MyOrderingClass explícitamente (en lugar de IComparer )
para establecer sus propiedades de clasificación:

MyOrderingClass comparer = new MyOrderingClass();  
comparer.DateDescending = ...;  
comparer.OrderIdDescending = ...;  
orderList.Sort(comparer);  
1
Jack Griffin

Desde el punto de vista del rendimiento, lo mejor es usar una lista ordenada para que los datos se ordenen a medida que se agregan al resultado. Otros enfoques necesitan al menos una iteración adicional en los datos y la mayoría crea una copia de los datos, por lo que no solo se verá afectado el rendimiento sino también el uso de la memoria. Puede que no sea un problema con un par de cientos de elementos, pero sí con miles, especialmente en servicios donde muchas solicitudes simultáneas pueden ordenarse al mismo tiempo. Eche un vistazo al espacio de nombres System.Collections.Generic y elija una clase con clasificación en lugar de Lista.

Y evite implementaciones genéricas utilizando la reflexión cuando sea posible, esto también puede causar problemas de rendimiento.

0
user3285954