Parent{ List<Child> Children {get;set;} }
Child { int Age {get;set;} }
我想在孩子最低年龄的时候订购父母,在领带的情况下,请向第二或第三个孩子订购。
我最接近的是这个,只有最小的孩子订购:
parents.OrderBy(p => p.Children.Min(c => c.Age))
在平局情况下,这并不是第二(或第三等)最年轻的。
鉴于这3名父母有相应的孩子年龄,我希望他们按此顺序出来。
答案 0 :(得分:3)
因此,您在概念层面尝试做的是比较两个序列。我们可以简单地编写一个能够比较任何两个序列的比较器,而不是尝试使用特殊序列的特殊情况。
它会遍历序列中的项目,比较同一位置的项目,然后如果找到一对不相等的项目,它就会知道结果。
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();
}
}
现在我们可以在调用OrderBy
时简单地使用此比较器:
var query = parents.OrderBy(parent => parent.Children
.OrderBy(child => child.Age)
.Select(child => child.Age)
, new SequenceComparer<int>());
答案 1 :(得分:2)
您需要编写类似此扩展方法的内容:
var orderedParents = parents.OrderBy(p => p.Children, c => c.Age);
通用实施:
/// <summary>
/// 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.
/// </summary>
public static IOrderedEnumerable<T> OrderBy<T, TKey, TValue>(this IEnumerable<T> @this, Func<T, IEnumerable<TKey>> getKeys, Func<TKey, TValue> getValue)
where TValue : IComparable<TValue>
{
return @this.OrderBy(x => x, new KeyComparer<T, TKey, TValue>(getKeys, getValue));
}
private class KeyComparer<T, TKey, TValue> : IComparer<T>
where TValue : IComparable<TValue>
{
private Func<T, IEnumerable<TKey>> GetKeys;
private Func<TKey, TValue> GetValue;
public KeyComparer(Func<T, IEnumerable<TKey>> getKeys, Func<TKey, TValue> 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());
}
}
答案 2 :(得分:0)
你可以使用ThenBy
并带走第二和第三个孩子。但是,这不可扩展,因此它取决于impl的需求
如果您想要更强大的功能,可以执行以下操作。它适用于这个特定情况。我将看看我是否可以优化它以使其更通用:)
public static class myExt
{
public static List<Parent> OrderByWithTieBreaker(this List<Parent> parents, int depth = 0)
{
if (depth > parents[0].Children.Count())
return parents;
var returnedList = new List<Parent>();
Func<Parent, int> keySelector = x =>
{
IEnumerable<Child> 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<Child> Children { get; set; } }
public class Child { public int Age {get;set;} }
[Test]
public void TestName()
{
var parents = new List<Parent>
{
new Parent{Name="P3", Children = new List<Child>{new Child{Age=1}, new Child{Age=3}, new Child{Age=6}, new Child{Age=7}}},
new Parent{Name="P4", Children = new List<Child>{new Child{Age=1}, new Child{Age=3}, new Child{Age=6}, new Child{Age=7}}},
new Parent{Name="P2", Children = new List<Child>{new Child{Age=1}, new Child{Age=3}, new Child{Age=6}}},
new Parent{Name="P1", Children = new List<Child>{new Child{Age=1}, new Child{Age=2}, new Child{Age=7}}},
new Parent{Name="P5", Children = new List<Child>{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++;
}
}