【问题标题】:Linq group by parent property order by childLinq 按父属性按子顺序分组
【发布时间】:2016-02-12 15:13:44
【问题描述】:

基本上我要做的是让父母按 Parent.type 分组,他们的 Child.type 的状态为“y”,按 child.Date 排序

作为一个例子,我创建了这些类:

public class Collection
{
    public IEnumerable<Parent> Parents { get; set; }
    public int Id { get; set; }
}

public class Parent
{
    public int Id { get; set; }
    public Type type { get; set; }       
    public IEnumerable<Child> Children { get; set; }
}

public class Child
{
    public int Id { get; set; }
    public Status Status { get; set; }
    public DateTime Date { get; set; }
}

public enum Type
{
    a,
    b
}

public enum Status
{
    x,
    y
}

我想要的是每个类型的父母列表,其中孩子的日期价值最高。

为了让这个例子顺利进行,我添加了一些测试数据:

Collection collection= new Collection
        {
            Id = 1,
            Parents = new List<Parent>
            {
                new Parent
                {
                    Id= 1,
                    type = Type.a,
                    Children = new List<Child>
                    {
                        new Child
                        {
                            Id = 1,
                            Status = Status.y,
                            Date = DateTime.Now.AddDays(-1)
                        },
                        new Child
                        {
                            Id = 2,
                            Status = Status.x,
                            Date = DateTime.Now.AddDays(-1)
                        }
                    }
                },
                new Parent
                {
                    Id= 2,
                    type = Type.b,
                    Children = new List<Child>
                    {
                        new Child
                        {
                            Id = 3,
                            Status = Status.y,
                            Date = DateTime.Now.AddDays(-2)
                        },
                        new Child
                        {
                            Id = 4,
                            Status = Status.x,
                            Date = DateTime.Now.AddDays(-2)
                        }
                    }
                },
                new Parent
                {
                    Id= 3,
                    type = Type.a,
                    Children = new List<Child>
                    {
                        new Child
                        {
                            Id = 5,
                            Status = Status.y,
                            Date = DateTime.Now.AddDays(-3)
                        }
                    }
                },
                new Parent
                {
                    Id= 4,
                    type = Type.b,
                    Children = new List<Child>
                    {
                        new Child
                        {
                            Id = 6,
                            Status = Status.y,
                            Date = DateTime.Now.AddDays(-3)
                        },
                        new Child
                        {
                            Id = 7,
                            Status = Status.y,
                            Date = DateTime.Now.AddDays(-4)
                        },
                        new Child
                        {
                            Id = 8,
                            Status = Status.x,
                            Date = DateTime.Now.AddDays(-4)
                        }
                    }
                },
            }
        };

选择的结果应该是一个包含 2 个父级的列表。每种类型( a 和 b )一个。这两个仍然包含它们的所有 Child 对象。

有什么简单的解决办法吗?

我尝试了类似的方法:

List<Parent> test = collection.Parents
                .GroupBy(m => m.type)
                .Select(
                m => m.OrderByDescending(
                    mm => mm.Children.Select(
                        r => r).OrderByDescending(
                            r => r.Date)).FirstOrDefault()).ToList();

但结果并不好。

=====更新=====

感谢 Enigmativity 的例子,这个问题得到了解决。我对 linq 查询做了一些修改,因为我使用的原始内容是由实体框架 (6) 提供的。这对我来说意味着所选的父对象不包含任何子对象,因此我必须选择新的对象类型(因为我们无法实例化实体框架模型提供的类型的新对象)。我的父母也在另一个 IEnumerable 中,所以我也必须在父母上使用 SelectMany。

这导致了这样的事情:(但这不适用于测试类)

var result =
    collection
        .SelectMany(c => c.Parents)
        .GroupBy(p => p.type)
        .SelectMany(gps =>
            gps
                .SelectMany(
                    gp => gp.Children.Where(c => c.Status == Status.y),
                    (gp, c) => new { gp, c })
                .OrderByDescending(gpc => gpc.c.Date)
                .Take(1)
                .Select(gpc => new ParentData {Id = gpc.gp.Id, Children= gpc.gp.Children}))
        .ToList();

【问题讨论】:

  • 干得好,这么新,用可编译的代码问了这么详细的问题。两次快速复制和粘贴,我就能够运行您的代码。我希望更多的新用户也一样好。

标签: c# linq


【解决方案1】:

如果我正确理解了您的要求,您希望按类型对所有父母进行分组,然后根据该父母在该组中的所有孩子中拥有一个日期最高的孩子,从每个组中只选择一个父母。

这是我想出的:

var result =
    collection
        .Parents
        .GroupBy(p => p.type)
        .SelectMany(gps =>
            gps
                .SelectMany(
                    gp => gp.Children.Where(c => c.Status == Status.y),
                    (gp, c) => new { gp, c })
                .OrderByDescending(gpc => gpc.c.Date)
                .Take(1)
                .Select(gpc => gpc.gp))
        .ToList();

这给了我:

【讨论】:

  • “我想要做的是让父母按 Parent.type 分组,他们的 Child.type 的状态为'y',由 child.Date 排序”。我没有在您的结果中看到这一点
  • @mariovalens - 已修复。
  • 还是错了。他想要所有状态为 y 的孩子。您只为选定的父级选择状态为 y 的子级。他真正想要的是我在回答中发布的内容,按类型折叠父母并汇总所有孩子的那种状态 y。
  • 这就是你需要创建新的父对象的原因
  • @mariovalens - 不,这不是我对问题的解读。 “这两个仍然包含它们所有的子对象”这一行意味着我们只是选择了现有的父对象。 OP 只想按Status.y 过滤,而不是选择。
【解决方案2】:

当应用查询/投影的父母是父母时,实际上无法过滤、排序等。

您必须:

  • 打破关系并投影到不同的数据结构(如字典或IGrouping,其中父项是键,排序后的子项是值)
  • 在不使用 LINQ 的情况下在循环中强制 Sort() 或重新分配 Children
  • 编写多行 lambda,其中第二行是副作用,例如排序或分配子项(不推荐)

就我个人而言,我认为只将您需要的数据投影到简化的表示中,而不是尝试将层次结构一直向下保留更容易。

【讨论】:

    【解决方案3】:

    我对这个问题的理解是,您需要创建一个按类型分组的父母列表,并为每个父母类型汇总所有状态为 y 的孩子。

    为此,请注意,您不能按 Parent 类型折叠列表并保留父结构(这意味着 Id 也必须折叠,我刚刚取了第一个值,您可以选择不同的值)。否则,这应该是您正在寻找的解决方案:

    var result = collection.Parents.GroupBy(c => c.type)
        .Select(g => new Parent
                     {
                         type = g.Key,      
                         Id = g.Select(p => p.Id).FirstOrDefault(),
                         Children = g.SelectMany(p => p.Children
                                  .Where(c => c.Status == Status.y)
                                  .OrderByDescending(c => c.Date)).ToList()                           
                     }).ToList();
    

    【讨论】:

    • "这两个仍然包含它们的所有子对象。" - 您的代码创建了新的父母并用错误的孩子填充他们。
    • Enigmativity,您是否尝试运行查询以查看孩子是否正确?他期望所有状态为'Y'的孩子,这就是作者所说的“所有孩子对象”的意思。是的,该查询创建了新的父对象,我已经解释了原因,并且没有说明他需要相同的父对象。
    • 我错过了Status.y 要求,我已经解决了我的代码问题。你的仍然不正确。
    • 您知道您的代码将每个类型的另一个父级的子级添加到您正在创建的那个吗?即使我们不同意 x 孩子是否被包括在内,那肯定是不对的吗?
    • 是的,正如我所说,我们对这个问题有不同的理解。让作者决定他的意思。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-26
    • 1970-01-01
    相关资源
    最近更新 更多