Linq OrderBy Sub List

本文关键字:List Sub OrderBy Linq | 更新日期: 2023-09-27 18:35:42

我正在尝试执行一个相当简单的命令,但似乎在为如何做到这一点而苦苦挣扎。以我有这两个类为例。

public class Method
{
    public int Id { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
    public List<Slot> Slots { get; set; }
}
public class Slot
{
    public DateTime ExpectedDeliveryDate { get; set; }
}

使用下面的代码,我想按最便宜的选项订购,然后按最快的交货日期订购。

var methods = new List<Method>();
methods.Add(new Method { Id = 1, Name = "Standard", Price = 0M, Slots = new List<Slot> { new Slot { ExpectedDeliveryDate = DateTime.Now.AddDays(5).Date } } });
methods.Add(new Method { Id = 2, Name = "Super Fast Next Day", Price = 0M, Slots = new List<Slot> { new Slot { ExpectedDeliveryDate = DateTime.Now.AddDays(1).Date } } });
var b = methods.OrderBy(x => x.Price)
    .ThenBy(y => y.Slots.OrderBy(t => t.ExpectedDeliveryDate.Date)
        .ThenBy(t => t.ExpectedDeliveryDate.TimeOfDay))
            .ToList();

在这里遇到的麻烦是我收到一个运行时错误,指出"至少一个对象必须实现 IComparable"。

虽然我可以通过实现 IComparable 接口来解决这个问题,但我想知道是否可以做到这一点。我想好像我有这段代码(见下文)它工作正常。

var slots = new List<Slot>();
slots.Add(new Slot { ExpectedDeliveryDate = DateTime.Now.AddDays(5).Date });
slots.Add(new Slot { ExpectedDeliveryDate = DateTime.Now.AddDays(1).Date });
slots.Add(new Slot { ExpectedDeliveryDate = DateTime.Now.AddDays(3).Date });
slots.Add(new Slot { ExpectedDeliveryDate = DateTime.Now.Date });
var d = slots.OrderBy(x => x.ExpectedDeliveryDate);

干杯,DS。

对于上述示例中的变量(例如xyz)的命名表示歉意:)可以复制和粘贴代码以获得操作乐趣。

编辑- 更新以简化代码示例。- 结果的预期将在成功排序后

Input
  ID     Name            Price    Slot
  1      Standard        0        DateTime.Now.AddDays(5).Date
  2      Super Fast      0        DateTime.Now.Date
Output
  2      Super Fast      0        DateTime.Now.Date  
  1      Standard        0        DateTime.Now.AddDays(5).Date

所以我的超快速选择应该是顶级的,因为它是最便宜的,当然也有最快的交货日期。

Linq OrderBy Sub List

您可以使用

Enumerable.Min()来挑选日期最早的插槽,如下所示:

        var query = deliveryMethods
            .OrderBy(x => x.Slots.Min(s => s.ExpectedDeliveryDate).Year)
            .ThenBy(x => x.Slots.Min(s => s.ExpectedDeliveryDate).Month)
            .ThenBy(x => x.Slots.Min(s => s.ExpectedDeliveryDate).Date)
            .ToList();

或者,只是

        var query = deliveryMethods
            .OrderBy(x => x.Slots.Min(s => s.ExpectedDeliveryDate.Date))
            .ToList();

请注意,当输入序列为空且要最小化的类型是值类型时,Min()将引发异常。 如果要避免异常,可以执行以下操作:

        var query2 = deliveryMethods
            .OrderBy(x => x.Slots.Min(s => (DateTime?)(s.ExpectedDeliveryDate.Date)))
            .ToList();

通过将DateTime转换为可为空,Min()将返回空序列的 null,并且具有空槽列表Method对象将排序到开头。

我想

解释一下为什么你在原始帖子中发布的尝试不起作用:

var xyz = deliveryMethods
        .OrderBy(x => x.Slots.OrderBy(y => y.ExpectedDeliveryDate.Year))
        .ThenBy(x => x.Slots.OrderBy(y => y.ExpectedDeliveryDate.Month))
        .ThenBy(x => x.Slots.OrderBy(y => y.ExpectedDeliveryDate.Date))
        .ToList();

这是因为你在OrderBy s内嵌套了OrderBy

x.Slots.OrderBy(...)产生一个IEnumerable<Slot>,所以你基本上是在告诉它"将这些IEnumerable<Slot>相互比较以决定交付方式的顺序"。但是Linq不知道如何将一个IEnumerable<Slot>与另一个进行比较,并决定哪个在另一个之前(IEnumerable<Slot>没有实现IComparable<T>),所以你得到了一个错误。

正如另一位用户指出的那样,答案是给它一些可以比较的东西。正如您后来澄清的那样,这将是每种交付方式的最早时段:

var xyz = deliveryMethods
        .OrderBy(x => x.Slots.Min(y => y.ExpectedDeliveryDate))
        .ToList();

这将在假设每个交付方法至少有一个插槽的情况下工作,但如果其中任何一个具有零插槽(或者如果Slots为 null),则会引发运行时异常。我已经问过你两次在这种情况下应该怎么做,我鼓励你澄清这一点。

一种可能的解决方案是仅包含具有插槽的交付方法:

var xyz = deliveryMethods
        .Where(x => x.Slots != null && x.Slots.Any())
        .OrderBy(x => x.Slots.Min(y => y.ExpectedDeliveryDate))
        .ToList();
相关文章: