如果我有两个这样的元组:
List<Tuple<T, List<Student>>> listOfTuplesOne;
List<Tuple<T, List<Student>>> listOfTuplesTwo;
我试图根据Student
对象的属性Student
找到相似的Student.Id
对象。因此,我想根据List<Student>
在listOfTuplesOne
的{{1}}和List<Student>
的{{1}}中找到所有匹配项。
我知道这是一个非常基本的问题,但是LINQ使我有些困惑。
我知道我不能做这样的事情:
listOfTuplesTwo
但是它应该接近我的需求。
答案 0 :(得分:1)
随着比较数量成倍增加,迭代一个列表,在另一个列表上执行Contains
或Any
的解决方案将遭受讨厌的性能特征。项目数。
匹配任务是由连接描述的最简洁的方法(对于LINQ而言,效率最高)。
因此,首先,让连接每一侧的所有学生进入单个序列。您可以使用SelectMany
进行展平:
var flattenedStudentsFromListOne = listOfTuplesOne.SelectMany(t => t.Item2);
var flattenedStudentsFromListTwo = listOfTuplesTwo.SelectMany(t => t.Item2);
现在,我们通过Id
属性将两个(非嵌套的)序列连接在一起:
var matchingStudents = flattenedStudentsFromListOne
.Join(
flattenedStudentsFromListTwo,
s1 => s1.Id,
s2 => s2.Id,
(studentOne, studentTwo) => new { studentOne, studentTwo });
例如,您还想查找未配对的商品,则需要FullOuterJoin
。遗憾的是,LINQ中没有标准实现,因此这是一对扩展方法,它们为标准Join
提供了相似的接口:
public static class JoinExtensions
{
public static IEnumerable<TResult> FullOuterJoin<TLeft, TRight, TKey, TResult>(
this IEnumerable<TLeft> leftSequence,
IEnumerable<TRight> rightSequence,
Func<TLeft, TKey> leftKeySelector,
Func<TRight, TKey> rightKeySelector,
Func<TLeft, TRight, TKey, TResult> projection,
TLeft defaultLeft = default(TLeft),
TRight defaultRight = default(TRight),
IEqualityComparer<TKey> comparer = null)
{
var cmp = comparer ?? EqualityComparer<TKey>.Default;
var leftLookup = leftSequence.ToLookup(leftKeySelector, cmp);
var rightLookup = rightSequence.ToLookup(rightKeySelector, cmp);
var allKeys = new HashSet<TKey>(
leftLookup.Select(p => p.Key).Concat(rightLookup.Select(p => p.Key)),
cmp);
return allKeys
.SelectMany(
key => leftLookup[key].DefaultIfEmpty(defaultLeft),
(key, leftItem) => new { key, leftItem })
.SelectMany(
x => rightLookup[x.key].DefaultIfEmpty(defaultRight),
(x, rightItem) => projection(x.leftItem, rightItem, x.key));
}
public static IEnumerable<TResult> FullOuterJoin<TLeft, TRight, TKey, TResult>(
this IEnumerable<TLeft> leftSequence,
IEnumerable<TRight> rightSequence,
Func<TLeft, TKey> leftKeySelector,
Func<TRight, TKey> rightKeySelector,
Func<TLeft, TRight, TResult> projection,
TLeft defaultLeft = default(TLeft),
TRight defaultRight = default(TRight),
IEqualityComparer<TKey> comparer = null)
{
return leftSequence
.FullOuterJoin(
rightSequence,
leftKeySelector,
rightKeySelector,
(leftItem, rightItem, _) => projection(leftItem, rightItem),
defaultLeft,
defaultRight,
comparer);
}
}
现在您可以:
var matchingStudents = flattenedStudentsFromListOne
.FullOuterJoin(
flattenedStudentsFromListTwo,
s1 => s1.Id,
s2 => s2.Id,
(studentOne, studentTwo) => new { studentOne, studentTwo });
以null
作为studentOne
或studentTwo
的值的返回项目表示在连接的另一端没有匹配项的学生。是的。
答案 1 :(得分:0)
有几种方法可以实现这一目标,例如,首先选择所有学生(一个和两个)进入新馆藏,然后执行以下操作:
var studentsOne = listOfTuplesOne.SelectMany(s => s.Item2);
var studentsTwo = listOfTuplesTwo.SelectMany(s => s.Item2);
var matchResult = studentsOne.Where(s => studentsTwo.Select(t => t.Id).Contains(s.Id));