按Linq中子集合中的最小值排序父集合

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

我想在孩子最低年龄的时候订购父母,在领带的情况下,请向第二个或第三个孩子办理。

我最接近的是这个,只有最小的孩子订购:

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

在并列的情况下,这不会占第二(或第三等)最年轻。

鉴于这3个父母有相应的孩子年龄,我希望他们按此顺序出来。

  • P1 1,2,7
  • P2 1,3,6
  • P3 1,4,5

因此,您在概念层面尝试做的是比较两个序列。 我们可以简单地编写一个能够比较任何两个序列的比较器,而不是尝试针对这个特定序列的特殊情况。

它会遍历序列中的项目,比较相同位置的项目,然后如果找到一对不相等的项目,它就会知道结果。

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

现在我们可以在调用OrderBy时简单地使用这个比较器:

 var query = parents.OrderBy(parent => parent.Children .OrderBy(child => child.Age) .Select(child => child.Age) , new SequenceComparer()); 

您需要编写类似此扩展方法的内容:

 var orderedParents = parents.OrderBy(p => p.Children, c => c.Age); 

通用实施:

 ///  /// Given a way to determine a collection of elements (for example /// children of a parent) and a comparable property of those items /// (for example age of a child) this orders a collection of elements /// according to the sorting order of the property of the first element /// of their respective collections. In case of a tie, fall back to /// subsequent elements as appropriate. ///  public static IOrderedEnumerable OrderBy(this IEnumerable @this, Func> getKeys, Func getValue) where TValue : IComparable { return @this.OrderBy(x => x, new KeyComparer(getKeys, getValue)); } private class KeyComparer : IComparer where TValue : IComparable { private Func> GetKeys; private Func GetValue; public KeyComparer(Func> getKeys, Func getValue) { this.GetKeys = getKeys; this.GetValue = getValue; } public int Compare(T x, T y) { var xKeys = GetKeys(x).OrderBy(GetValue).Select(GetValue); var yKeys = GetKeys(y).OrderBy(GetValue).Select(GetValue); foreach (var pair in xKeys.Zip(yKeys, Tuple.Create)) { if (pair.Item1.CompareTo(pair.Item2) != 0) return pair.Item1.CompareTo(pair.Item2); } return xKeys.Count().CompareTo(yKeys.Count()); } } 

您可以使用ThenBy并带走第二个和第三个孩子。 但是,这不可扩展,因此它取决于impl的需求

如果您想要更强大的东西,您可以执行以下操作。 它适用于这个特定情况。 我将看看我是否可以优化它以更通用:)

 public static class myExt { public static List OrderByWithTieBreaker(this List parents, int depth = 0) { if (depth > parents[0].Children.Count()) return parents; var returnedList = new List(); Func keySelector = x => { IEnumerable enumerable = x.Children.OrderBy(y => y.Age).Skip(depth); if (!enumerable.Any()) return 0; //If no children left, then return lowest possible age return enumerable.Min(z => z.Age); }; var orderedParents = parents.OrderBy(keySelector); var groupings = orderedParents.GroupBy(keySelector); foreach (var grouping in groupings) { if (grouping.Count() > 1) { var innerOrder = grouping.ToList().OrderByWithTieBreaker(depth + 1); returnedList = returnedList.Union(innerOrder).ToList(); } else returnedList.Add(grouping.First()); } return returnedList; } } [TestFixture] public class TestClass { public class Parent { public string Name { get; set; } public List Children { get; set; } } public class Child { public int Age {get;set;} } [Test] public void TestName() { var parents = new List { new Parent{Name="P3", Children = new List{new Child{Age=1}, new Child{Age=3}, new Child{Age=6}, new Child{Age=7}}}, new Parent{Name="P4", Children = new List{new Child{Age=1}, new Child{Age=3}, new Child{Age=6}, new Child{Age=7}}}, new Parent{Name="P2", Children = new List{new Child{Age=1}, new Child{Age=3}, new Child{Age=6}}}, new Parent{Name="P1", Children = new List{new Child{Age=1}, new Child{Age=2}, new Child{Age=7}}}, new Parent{Name="P5", Children = new List{new Child{Age=1}, new Child{Age=4}, new Child{Age=5}}} }; var f = parents.OrderByWithTieBreaker(); int count = 1; foreach (var d in f) { Assert.That(d.Name, Is.EqualTo("P"+count)); count++; } }