如何检查两个元组的Item2列表是否具有相似的值?

时间:2018-10-17 12:54:48

标签: c# linq

如果我有两个这样的元组:

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

但是它应该接近我的需求。

2 个答案:

答案 0 :(得分:1)

随着比较数量成倍增加,迭代一个列表,在另一个列表上执行ContainsAny的解决方案将遭受讨厌的性能特征。项目数。

匹配任务是由连接描述的最简洁的方法(对于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作为studentOnestudentTwo的值的返回项目表示在连接的另一端没有匹配项的学生。是的。

答案 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));