Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Linq group by parent property order by child

Tags:

c#

linq

Basicly what i am trying to do is getting the parent grouped by Parent.type where their Child.type has status 'y' ordered by child.Date

As an example i created these classes:

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
}

What i want is a list of Parents of each type where the Child's Date is highest in value.

To get this example going i added some test data:

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

The result of the select should be a List containing 2 Parents. One for each type ( a and b ). Both of these still containing all of their Child objects.

Is there any simple solution for this ?

I tried something like:

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

But that didn't end well.

=====UPDATE=====

Thanks to the example of Enigmativity this issue is solved. I made a slight modification to the linq query as the original content i am using is provided by Entity Framework (6). This meant for me that the selected Parent object was not containing any Children, so i had to select into a new object type (As we can't instantiate a new object of the type provided by the Entity Framework model) . Also my Parents were in another IEnumerable so I had to use a SelectMany on the Parents as well.

This resulted in something like this: (This won't work with the test classes though)

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();
like image 957
Kevin Avatar asked Feb 11 '16 21:02

Kevin


1 Answers

If I've understood your requirement correctly you want to group all of the parents by type and then chose only one parent from each group based on that parent having a child with the highest date from all the children in that group.

Here's what I came up with:

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

That gives me:

result

like image 114
Enigmativity Avatar answered Nov 08 '22 07:11

Enigmativity