C# 在Linq中按子集合中的最小值对父集合排序
在平局的情况下,这并不能解释第二(或第三等)小的原因 考虑到这3位家长的孩子年龄相对应,我希望他们按照这个顺序出现C# 在Linq中按子集合中的最小值对父集合排序,c#,linq,C#,Linq,在平局的情况下,这并不能解释第二(或第三等)小的原因 考虑到这3位家长的孩子年龄相对应,我希望他们按照这个顺序出现 P1 1,2,7 P2 1,3,6 P3 1,4,5 您可以使用并带走第二个和第三个孩子。但是,这是不可伸缩的,因此它取决于impl的需要 如果您想要更健壮的东西,可以执行以下操作。它将适用于此特定情况。不过,我要看看是否可以将其优化为更通用:) 公共静态类myExt { 公共静态列表OrderByWithTieBreaker(此列表父级,int-depth=0) { 如果(深度
- P1 1,2,7
- P2 1,3,6
- P3 1,4,5
- 您可以使用并带走第二个和第三个孩子。但是,这是不可伸缩的,因此它取决于impl的需要
如果您想要更健壮的东西,可以执行以下操作。它将适用于此特定情况。不过,我要看看是否可以将其优化为更通用:)
公共静态类myExt
{
公共静态列表OrderByWithTieBreaker(此列表父级,int-depth=0)
{
如果(深度>父项[0]。子项.Count())
返回父母;
var returnedList=新列表();
Func keySelector=x=>
{
IEnumerable enumerable=x.Children.OrderBy(y=>y.Age).Skip(depth);
如果(!enumerable.Any())
返回0;//如果没有孩子,则返回可能的最低年龄
返回enumerable.Min(z=>z.Age);
};
var orderedParents=parents.OrderBy(keySelector);
var groupings=orderedParents.GroupBy(keySelector);
foreach(分组中的var分组)
{
if(grouping.Count()>1)
{
var innerOrder=grouping.ToList().OrderByWithTieBreaker(深度+1);
returnedList=returnedList.Union(innerOrder.ToList();
}
其他的
returnedList.Add(grouping.First());
}
返回列表;
}
}
[测试夹具]
公共类TestClass
{
公共类父级{public string Name{get;set;}公共列表子级{get;set;}
公共类子{public int Age{get;set;}}
[测试]
public void TestName()
{
var parents=新列表
{
新父项{Name=“P3”,子项=新列表{new Child{Age=1},新子项{Age=3},新子项{Age=6},新子项{Age=7},
新父项{Name=“P4”,子项=新列表{新子项{Age=1},新子项{Age=3},新子项{Age=6},新子项{Age=7},
新父项{Name=“P2”,子项=新列表{new Child{Age=1},新子项{Age=3},新子项{Age=6}},
新父项{Name=“P1”,子项=新列表{new Child{Age=1},新子项{Age=2},新子项{Age=7}},
新父项{Name=“P5”,子项=新列表{新子项{Age=1},新子项{Age=4},新子项{Age=5}}
};
var f=parents.OrderByWithTieBreaker();
整数计数=1;
foreach(f中的变量d)
{
断言(d.Name,Is.EqualTo(“P”+count));
计数++;
}
}
您需要编写类似以下扩展方法的内容:
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++;
}
}
一般实施:
var orderedParents = parents.OrderBy(p => p.Children, c => c.Age);
//
///给出了确定元素集合的方法(例如
///父母的子女)以及这些项目的可比财产
///(例如,孩子的年龄)这会对元素集合进行排序
///根据第一个元素属性的排序顺序
///它们各自的系列。如果出现平局,请退回到
///后续要素(视情况而定)。
///
公共静态IOrderedEnumerable OrderBy(this IEnumerable@this,Func getKeys,Func getValue)
其中TValue:i可比较
{
return@this.OrderBy(x=>x,newkeycomarer(getKeys,getValue));
}
私有类密钥比较器:IComparer
其中TValue:i可比较
{
私钥;
私有函数GetValue;
公钥比较器(Func-getKeys,Func-getValue)
{
this.GetKeys=GetKeys;
this.GetValue=GetValue;
}
公共整数比较(TX,TY)
{
var xKeys=GetKeys(x).OrderBy(GetValue).Select(GetValue);
var yKeys=GetKeys(y).OrderBy(GetValue).Select(GetValue);
foreach(xKeys.Zip(yKeys,Tuple.Create)中的var对)
{
如果(对项目1.比较到(对项目2)!=0)
返回pair.Item1.CompareTo(pair.Item2);
}
返回xKeys.Count().CompareTo(yKeys.Count());
}
}
因此,在概念层面上,您要做的是比较两个序列。与其尝试对这个特定序列进行特殊处理,我们只需编写一个能够比较任意两个序列的比较器
它将遍历序列中的项目,比较相同位置的项目,然后如果发现一对不相等的项目,它就会知道结果
/// <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());
}
}
以上内容已经在以下数据上进行了测试:
A(4,1,2)B(2,4)C(2,2)D()
排序为D A C B
zip的良好使用。我在测试中检查了我能想到的所有边缘情况:)可能的重复。具体来说,您可以从那里使用实现作为比较
方法:parents.OrderBy(p=>p.Children.Select(x=>x.Age).ToList(),new SequenceComparer())
(其中SequenceComparer.Compare
是该链接上的实现)+1,Compare
函数非常简洁……我想序列长度不同的情况除外。@Rawling是的,你可以添加.DefaultIfEmpty(x.Count().CompareTo(y.Count()))
就在where to cover之后,但这是两次完全迭代两个序列,这比需要做的工作多得多。唯一的替代方法是放弃整个LINQ解决方案,全部通过手动迭代完成,这是我想要避免的。
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());
}
}
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();
}
}
var query = parents.OrderBy(parent => parent.Children
.OrderBy(child => child.Age)
.Select(child => child.Age)
, new SequenceComparer<int>());