【问题标题】:achieving a complex sort via Linq to Objects通过 Linq to Objects 实现复杂排序
【发布时间】:2009-11-16 15:34:21
【问题描述】:

有人要求我对数据集应用条件排序,我正试图弄清楚如何通过 LINQ 实现这一点。在此特定域中,采购订单可以标记为主要或次要。用于确定主要/次要状态的确切机制相当复杂,并且与手头的问题无关。

考虑下面的数据集。

Purchase Order    Ship Date       Shipping Address     Total
6                  1/16/2006       Tallahassee FL      500.45 
19.1             2/25/2006       Milwaukee WI        255.69 
5.1              4/11/2006       Chicago IL          199.99 
8                  5/16/2006       Fresno CA           458.22 
19                7/3/2006        Seattle WA          151.55
5                   5/1/2006        Avery UT            788.52    
5.2                 8/22/2006       Rice Lake MO        655.00 

辅助 PO 是带有十进制数字的那些,而主要 PO 是那些带有整数的那些。我正在处理的要求规定,当用户选择对给定列进行排序时,排序应该仅应用于主要 PO。出于排序目的,次要采购订单被忽略,但仍应按发货日期降序排列在其主要采购订单下方。

例如,假设用户按送货地址升序排序。数据将按如下方式排序。请注意,如果您忽略辅助采购订单,数据将按地址升序排序(艾弗里、弗雷斯诺、西雅图、塔拉哈西)

Purchase Order      Ship Date       Shipping Address     Total
5                   5/1/2006        Avery UT            788.52  
--5.2               8/22/2006       Rice Lake MO        655.00  
--5.1                4/11/2006       Chicago IL          199.99 
8                   5/16/2006       Fresno CA           458.22 
19                  7/3/2006        Seattle WA          151.55
--19.1               2/25/2006       Milwaukee WI        255.69 
6                   1/16/2006       Tallahassee FL      500.45 

有没有办法使用 OrderBy 扩展方法来达到预期的效果?还是我被卡住了(最好是)将排序分别应用于两个数据集,然后合并到一个结果集中?

public IList<PurchaseOrder> ApplySort(bool sortAsc)
{
    var primary = purchaseOrders.Where(po => po.IsPrimary)
                                .OrderBy(po => po.ShippingAddress).ToList();
    var secondary = purchaseOrders.Where(po => !po.IsPrimary)
                                  .OrderByDescending(po => po.ShipDate).ToList();
    //merge 2 lists somehow so that secondary POs are inserted after their primary
}

【问题讨论】:

    标签: linq sorting merge linq-group


    【解决方案1】:

    你见过 ThenBy 和 ThenByDescending 方法吗?

    purchaseOrders.Where(po => po.IsPrimary).OrderBy(po => po.ShippingAddress).ThenByDescending(x=>x.ShipDate).ToList();
    

    我不确定这是否能满足您的需求,因为我不太了解最终列表的外观(po.IsPrimary 和 !po.IsPrimary 让我感到困惑)。

    【讨论】:

    • 我认为这不是答案。仅当Order 的多个值相等时,ThenBy 才会起作用。
    • 我同意,但就像我说的那样,我觉得最终列表应该如何排序还不够清楚。所以,我想给他一个关于 ThenBy 方法的想法,这可能会有所帮助。
    【解决方案2】:

    您的问题的解决方案是GroupBy。

    首先根据所选列对您的对象进行排序:

    var ordered = purchaseOrders.OrderBy(po => po.ShippingAddress);
    

    您需要根据主要订单对订单进行分组。我假设订单是一个字符串,所以我创建了一个字符串IEqualityComparer,如下所示:

    class OrderComparer : IEqualityComparer<string>
    {
        public bool Equals(string x, string y)
        {
            x = x.Contains('.') ? x.Substring(0, x.IndexOf('.')) : x;
            y = y.Contains('.') ? y.Substring(0, y.IndexOf('.')) : y;
    
            return x.Equals(y);
        }
    
        public int GetHashCode(string obj)
        {
            return obj.Contains('.') ? obj.Substring(0, obj.IndexOf('.')).GetHashCode() : obj.GetHashCode();
        }
    }
    

    并使用它对订单进行分组:

    var grouped = ordered.GroupBy(po => po.Order, new OrderComparer());
    

    结果是一个按 ShippingAddress 列排序并按主要订单 ID 分组的树状结构。

    【讨论】:

    • 好极了。这非常接近我的需要。我仍然需要在每个分组中对 ShipDate 上的次要事件进行排序。有什么想法吗?
    猜你喜欢
    • 1970-01-01
    • 2018-03-03
    • 1970-01-01
    • 2011-11-30
    • 2011-11-11
    • 1970-01-01
    • 2011-10-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多