Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Order parent collection by minimum values in child collection in Linq

Tags:

c#

linq

Parent{ List<Child> Children {get;set;} }
Child { int Age {get;set;} }

I would like to order the parents by the lowest age of their children, proceeding to the second or third child in the case of a tie.

The closest I've come is this, which only orders by the youngest child:

parents.OrderBy(p => p.Children.Min(c => c.Age))

This doesn't account for second (or third, etc) youngest in the case of a tie.

Given these 3 parents with corresponding child ages, I'd like them to come out in this order.

  • P1 1,2,7
  • P2 1,3,6
  • P3 1,4,5
like image 840
Dan Avatar asked Dec 12 '13 19:12

Dan


1 Answers

So what you're trying to do, at a conceptual level, is compare two sequences. Rather than trying to special case it for this specific sequence, we can simply write a comparer capable of comparing any two sequences.

It will go through the items in the sequence compare the items at the same position, and then if it finds a pair that aren't equal, it knows the result.

public class SequenceComparer<TSource> : IComparer<IEnumerable<TSource>>
{
    private IComparer<TSource> comparer;
    public SequenceComparer(IComparer<TSource> comparer = null)
    {
        this.comparer = comparer ?? Comparer<TSource>.Default;
    }
    public int Compare(IEnumerable<TSource> x, IEnumerable<TSource> y)
    {
        return x.Zip(y, (a, b) => comparer.Compare(a, b))
                .Where(n => n != 0)
                .DefaultIfEmpty(x.Count().CompareTo(y.Count()))
                .First();
    }
}

Now we can simply use this comparer when calling OrderBy:

var query = parents.OrderBy(parent => parent.Children
    .OrderBy(child => child.Age)
    .Select(child => child.Age)
    , new SequenceComparer<int>());
like image 98
Servy Avatar answered Sep 28 '22 21:09

Servy