使用LINQ来对象在特定属性上相交和除外

时间:2010-10-22 15:14:24

标签: c# linq-to-objects intersect

当我有2个List<string>个对象时,我可以直接使用IntersectExcept来获取输出IEnumerable<string>。这很简单,但是如果我想要更复杂的东西的交叉/分离呢?

示例,尝试获取ClassA对象ClassA对象的AStr1ClassB对象的BStr上的交叉结果的public class ClassA { public string AStr1 { get; set; } public string AStr2 { get; set; } public int AInt { get; set; } } public class ClassB { public string BStr { get; set; } public int BInt { get; set; } } public class Whatever { public void xyz(List<ClassA> aObj, List<ClassB> bObj) { // *** this line is horribly incorrect *** IEnumberable<ClassA> result = aObj.Intersect(bObj).Where(a, b => a.AStr1 == b.BStr); } } 个对象的集合; :

{{1}}

如何修复注意到的线以实现此交叉。

3 个答案:

答案 0 :(得分:12)

MoreLINQExceptBy。它还没有IntersectBy,但您可以轻松编写自己的实现,甚至可能在之后将其贡献给MoreLINQ:)

它可能看起来像这样 (省略错误检查):

public static IEnumerable<TSource> IntersectBy<TSource, TKey>(
    this IEnumerable<TSource> first,
    IEnumerable<TSource> second,
    Func<TSource, TKey> keySelector,
    IEqualityComparer<TKey> keyComparer)
{
    HashSet<TKey> keys = new HashSet<TKey>(first.Select(keySelector),
                                           keyComparer);
    foreach (var element in second)
    {
        TKey key = keySelector(element);
        // Remove the key so we only yield once
        if (keys.Remove(key))
        {
            yield return element;
        }
    }
}

如果你想在两个完全不同的类型上执行一个碰巧有一个共同属性类型的交集,你可以使用三个类型参数制作一个更通用的方法(一个用于first,一个用于{{1}一个用于公共密钥类型)。

答案 1 :(得分:3)

x∈A∩B当且仅当x∈A且x∈B时才有。

因此,对于a中的每个aObj,您可以检查a.AStr1值是否属于BStr

public void xyz(List<ClassA> aObj, List<ClassB> bObj)
{
    HashSet<string> bstr = new HashSet<string>(bObj.Select(b => b.BStr));
    IEnumerable<ClassA> result = aObj.Where(a => bstr.Contains(a.AStr1));
}

答案 2 :(得分:1)

此代码:

    public IEnumerable<ClassA> xyz(List<ClassA> aObj, List<ClassB> bObj)
    {
        IEnumerable<string> bStrs = bObj.Select(b => b.BStr).Distinct();
        return aObj.Join(bStrs, a => a.AStr1, b => b, (a, b) => a);
    }

已通过以下测试:

    [TestMethod]
    public void PropertyIntersectionBasedJoin()
    {
        List<ClassA> aObj = new List<ClassA>()
                                {
                                    new ClassA() { AStr1 = "a" }, 
                                    new ClassA() { AStr1 = "b" }, 
                                    new ClassA() { AStr1 = "c" }
                                };
        List<ClassB> bObj = new List<ClassB>()
                                {
                                    new ClassB() { BStr = "b" }, 
                                    new ClassB() { BStr = "b" }, 
                                    new ClassB() { BStr = "c" }, 
                                    new ClassB() { BStr = "d" }
                                };

        var result = xyz(aObj, bObj);

        Assert.AreEqual(2, result.Count());
        Assert.IsFalse(result.Any(a => a.AStr1 == "a"));
        Assert.IsTrue(result.Any(a => a.AStr1 == "b"));
        Assert.IsTrue(result.Any(a => a.AStr1 == "c"));
    }