ChatGPT解决这个技术问题 Extra ChatGPT

如何按对象中的属性对 List<T> 进行排序

我有一个名为 Order 的类,它具有 OrderIdOrderDateQuantityTotal 等属性。我有这个 Order 类的列表:

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

我想根据 Order 对象的一个属性对列表进行排序;例如,按订单日期或订单 ID。

我怎样才能在 C# 中做到这一点?


K
Kyle Alons

我能想到的最简单的方法是使用 Linq:

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

我怎样才能按降序排序。
@BonusKun List SortedList = objListOrder。 OrderByDescending (o=>o.OrderDate).ToList();
请注意,这会创建一个包含内存中所有项目的全新列表,这在性能方面可能存在问题。
@staafl listWithObjects = listWithObjects.OrderByDescending(o => o.Status).ToList(); 足以完成这样的努力吗?
@staafl我们正在订购对象引用列表,据我所知,不会复制对象本身。虽然这确实使引用列表使用的内存增加了一倍,但它并不像实际复制所有对象本身那么糟糕,所以在大多数情况下,除了我们处理大量数据集并将它们保存在内存中已经是一个问题,那么应该足够了。
K
Kolappan N

如果您需要对列表进行就地排序,则可以使用 Sort 方法,传递 Comparison<T> 委托:

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

如果您更喜欢创建一个新的排序序列而不是就地排序,那么您可以使用 LINQ 的 OrderBy 方法,如其他答案中所述。


是的,这是“正确”的答案,应该比创建新的 IEnumerable 然后将其转换为新列表更有效。
当然,如果您需要降序排序,请将箭头 => 右侧的 xy 交换。
真正对列表进行就地排序的真正答案
@PimBrouwers 这是更好的选择期。即使您使用的内存量不是问题,此解决方案也可以避免不必要的内存分配,这非常昂贵。这个选项在代码方面同样简单,而且速度快了一个数量级。
@JonSchneider 对于 Nullable<> (以 DateTime? 为例)您可以使用 .Sort((x, y) => Nullable.Compare(x.OrderDate, y.OrderDate)) 它将 null 视为所有非 null 值之前。它与 .Sort((x, y) => Comparer<DateTime?>.Default.Compare(x.OrderDate, y.OrderDate) 完全相同。
C
Community

要在 .Net2.0 上不使用 LINQ 执行此操作:

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

如果您使用的是 .Net3.0,那么 LukeH 的 answer 就是您所追求的。

要对多个属性进行排序,您仍然可以在委托中进行。例如:

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

这将为您提供降序 orderIds 的升序日期。

但是,我不建议坚持代表,因为这意味着很多地方没有代码重用。您应该实现一个 IComparer 并将其传递给您的 Sort 方法。请参阅here

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

然后要使用这个 IComparer 类,只需实例化它并将其传递给您的 Sort 方法:

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

很好的答案,应该是正确的答案,因为它可以保护重新初始化原始列表(LINQ 版本将始终这样做)提供更好的封装。
@馄饨,不。这个想法是能够有不同的 IComparer 实现,从而为我们提供多态行为。
P
PSK

排序列表的最简单方法是使用 OrderBy

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

如果您想按多列排序,例如以下 SQL 查询。

ORDER BY OrderDate, OrderId

为此,您可以使用 ThenBy,如下所示。

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

J
Jimmy Hoffa

如您所说,在没有 Linq 的情况下这样做:

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

然后只需在您的订单列表中调用 .sort()


必须首先在 as 演员表上测试 null。这是 as 的全部要点,因为 (ha, ha) (Order)obj 在失败时会引发异常。 if(orderToCompare == null) return 1;
+1 用于使用接口,这使代码更易于维护并清楚地公开对象的功能。
如果 Bill 和 Ted 出现,我会要求他们带我回到 2014 年 10 月 16 日,这样我就可以更正上面的错误 - 如果转换失败,as 返回 null。但至少空测试是正确的。
@radarbob 是的.. 哎呀。 :) 但!此函数旨在通过对 List<Order> 列表的排序自动使用,因此应保证类型与 as 匹配,因此 2014 年您可能没有编写错误,就像避免不必要的守卫一样陈述 :)
应该保证 有趣的一点。如果它被很好地封装以至于它不能被调用,除非传递一个 List<Order>;但是你和我都遇到过自我封装的程序员,他不言而喻的假设是“我正在编写这段代码,所以它不会被错误使用”
r
radarbob

经典的面向对象解决方案

首先,我必须为 LINQ 的威力而跪下……现在我们已经解决了这个问题

JimmyHoffa 答案的变体。使用泛型,CompareTo 参数变得类型安全。

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

这个默认的可排序性当然是可重用的。也就是说,每个客户端都不必冗余地重写排序逻辑。交换“1”和“-1”(或逻辑运算符,您的选择)会反转排序顺序。


对列表中的对象进行排序的简单方法。但我不明白你为什么返回 1 if (that == null)?
这意味着 this 对象大于 null。出于排序的目的,空对象引用“小于”this 对象。这就是我决定定义空值如何排序的方式。
r
roger

// 用于 gridview 的完全通用排序

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

或者,您知道,data.OrderBy()。重新发明轮子更容易一些。
这个想法是这将适用于任何类型的对象。其中 OrderBy() 仅适用于强类型对象。
D
Daniel A. White

使用 LINQ

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

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

P
Peter

这是一个通用的 LINQ 扩展方法,它不会创建列表的额外副本:

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

要使用它:

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

我最近构建了这个额外的接受 ICompare<U>,以便您可以自定义比较。当我需要进行自然字符串排序时,这会派上用场:

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

我已经实现了这个,工作正常。我添加了一个“isAscending = true”参数。要按降序排序,只需在两个 Invoke() 方法中交换 x 和 y。谢谢。
如果您只是要编译表达式,那么您不妨先接受一个委托。此外,如果选择器导致副作用、计算成本高或不确定,则此方法存在重大问题。基于所选表达式的正确排序需要对列表中的每个项目不超过一次调用选择器。
@Servy - 这些都是有效的观点,但老实说,我不确定如何实施你的一些建议(特别是阻止选择器引起副作用......?)。我已将它们更改为代表。如果您知道如何进行其中一些更改,我很乐意让您编辑我的代码。
@Peter 你不能阻止代表造成副作用。您可以做的是确保每个对象不会多次调用它们,这意味着计算每个对象的值,存储对象/投影值对,然后对 that 进行排序。 OrderBy 在内部完成所有这些工作。
以这种精神编写 void 扩展方法的另一种好方法:static class Extensions { public static void SortBy<TSource, TKey>(this List<TSource> self, Func<TSource, TKey> keySelector) { self.SortBy(keySelector, Comparer<TKey>.Default); } public static void SortBy<TSource, TKey>(this List<TSource> self, Func<TSource, TKey> keySelector, IComparer<TKey> comparer) { self.Sort((x, y) => comparer.Compare(keySelector(x), keySelector(y))); } } 可以由 SortByDescending 方法补充。 @Servy 的评论也适用于我的代码!
W
Waqas Ahmed
//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;

m
molbalga

请让我用一些示例代码完成@LukeH 的答案,因为我已经对其进行了测试,我相信它可能对某些人有用:

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

J
Jan Černý

任何使用可空类型的人,Value 都需要使用 CompareTo

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

M
Miguel Becerra

Roger 版本的改进版。

GetDynamicSortProperty 的问题是只获取属性名称,但是如果在 GridView 中我们使用 NavigationProperties 会发生什么?它将发送一个异常,因为它发现 null。

例子:

“Employee.Company.Name;”会崩溃...因为只允许“Name”作为参数来获取它的值。

这是一个改进的版本,允许我们按导航属性进行排序。

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


    } 

D
Danny Mor

您可以对属性选择做一些更通用的事情,但具体说明您选择的类型,在您的情况下为“订单”:

将您的函数编写为通用函数:

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

然后像这样使用它:

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

您可以更通用并为您想要订购的内容定义一个开放类型:

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

并以相同的方式使用它:

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

这是一种愚蠢且不必要的复杂方式来执行 LINQ 样式的“OrderBy”,但它可能会为您提供如何以通用方式实现它的线索


J
Jevgenij Kononov
var obj = db.Items.Where...

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

S
Seyedraouf Modarresi

假设您有以下代码,在此代码中,我们有一个带有几个属性的 Passenger 类,我们希望根据这些属性进行排序。

public class Passenger
{
    public string Name { get; }
    public string LastName { get; }
    public string PassportNo { get; }
    public string Nationality { get; }

    public Passenger(string name, string lastName, string passportNo, string nationality)
    {
        this.Name = name;
        this.LastName = lastName;
        this.PassportNo = passportNo;
        this.Nationality = nationality;
    }

    public static int CompareByName(Passenger passenger1, Passenger passenger2)
    {
        return String.Compare(passenger1.Name, passenger2.Name);
    }

    public static int CompareByLastName(Passenger passenger1, Passenger passenger2)
    {
        return String.Compare(passenger1.LastName, passenger2.LastName);
    }

    public static int CompareNationality(Passenger passenger1, Passenger passenger2)
    {
        return String.Compare(passenger1.Nationality, passenger2.Nationality);
    }
}

public class TestPassengerSort
{
    Passenger p1 = new Passenger("Johon", "Floid", "A123456789", "USA");
    Passenger p2 = new Passenger("Jo", "Sina", "A987463215", "UAE");
    Passenger p3 = new Passenger("Ped", "Zoola", "A987855215", "Italy");

    public void SortThem()
    {
        Passenger[] passengers = new Passenger[] { p1, p2, p3 };
        List<Passenger> passengerList = new List<Passenger> { p1, p2, p3 };

        Array.Sort(passengers, Passenger.CompareByName);
        Array.Sort(passengers, Passenger.CompareByLastName);
        Array.Sort(passengers, Passenger.CompareNationality);

        passengerList.Sort(Passenger.CompareByName);
        passengerList.Sort(Passenger.CompareByLastName);
        passengerList.Sort(Passenger.CompareNationality);

    }
}

因此,您可以使用组合委托来实现您的排序结构。


P
Pranay Rana

使用 LiNQ OrderBy

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

J
Jack Griffin

基于 GenericTypeTea 的 Comparer :我们可以通过添加排序标志来获得更大的灵活性:

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

在这种情况下,您必须将其显式实例化为 MyOrderingClass (而不是 IComparer )才能设置其排序属性:

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

i
itcropper

以上答案对我来说都不够通用,所以我做了这个:

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

不过要小心处理海量数据集。这是简单的代码,但如果集合很大并且集合的对象类型具有大量字段,则可能会给您带来麻烦。运行时间为 NxM,其中:

N = 集合中的元素数

= 对象内的属性数


D
Dev-lop-er

如果您需要对问题实体中的字符串 ID 进行排序

解析Id值后使用Sort函数和delegate对Id进行排序

    class Question
    {
        public List<QuestionInfo> Questions Info{ get; set; }
    
    }

    class QuestionInfo
    {
        public string Id{ get; set; }
        public string Questions{ get; set; }
    
    }

    var questionnaire = new Question();
     questionnaire.QuestionInfo.Sort((x, y) => int.Parse(x.Id, CultureInfo.CurrentCulture) - int.Parse(y.Id, CultureInfo.CurrentCulture));


B
Bishiba

我为 List<T> 制作了这个扩展方法。

扩展方法将您希望排序的属性作为解析字符串,然后使用 List<T>OrderBy 方法。然后它将原始列表的每个索引设置为有序列表的相同索引。

public static class ListExtensions {
    public static void SortBy<T>(this List<T> list, string property, bool reverse = false) {
        List<T> ordered = list.OrderBy(obj => obj.GetType().GetProperty(property).GetValue(obj, null)).ToList();
            
        for (int i = 0; i < list.Count; i++)
            list[i] = reverse ? ordered[list.Count - 1 - i] : ordered[i];
    }
}

如果列表中的对象具有属性 Name,则您对列表 testList 进行排序,如下所示:

//For normal sorting order
testList.SortBy("Name");
//For reverse sorting order
testList.SortBy("Name", true);

我建议您将 SortBy 的名称更改为 Prefix_SortBy。如果您导入另一个库,以防止潜在的冲突。

我知道这种方法适用于字母和数字排序。它的分拣能力可能有限,但操作非常简单。

如果有一些重大缺陷或问题,请告诉我,我已经编写 C# 大约 3 个月了。

此致


u
user3285954

从性能的角度来看,最好的方法是使用排序列表,以便数据在添加到结果时进行排序。其他方法至少需要对数据进行一次额外的迭代,并且大多数方法会创建数据副本,因此不仅性能而且内存使用也会受到影响。对于数百个元素可能不是问题,但对于数千个元素可能不是问题,尤其是在许多并发请求可能同时进行排序的服务中。查看 System.Collections.Generic 命名空间并选择一个具有排序功能的类而不是 List。

并尽可能避免使用反射的通用实现,这也可能导致性能问题。


这怎么能更高效?将数据插入排序列表是 O(n),因此添加 n 项是 O(n^2)。许多并发项目试图添加什么?在某些情况下,您在添加项目时需要消耗额外的 CPU 周期,但这不是一个好的通用解决方案。
l
luka

嗨,只是回到这个问题。如果要对这个序列“1”“10”“100”“200”“2”“20”“3”“30”“300”的List进行排序,得到1;2;3这种形式的排序项;10;20;30;100;200;300 你可以使用这个:

 public class OrderingAscending : IComparer<String>
    {
        public int Compare(String x, String y)
        {
            Int32.TryParse(x, out var xtmp);
            Int32.TryParse(y, out var ytmp);

            int comparedItem = xtmp.CompareTo(ytmp);
            return comparedItem;
        }
    }

您可以以这种形式在后面的代码中使用它:

 IComparer<String> comparerHandle = new OrderingAscending();
 yourList.Sort(comparerHandle);