简体   繁体   English

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

[英]Using LINQ to objects Intersect and Except on a specific property

When I have 2 List<string> objects, then I can use Intersect and Except on them directly to get an output IEnumerable<string> . 当我有2个List<string>对象时,我可以直接使用IntersectExcept来获得输出IEnumerable<string> That's simple enough, but what if I want the intersection/disjuction on something more complex? 这很简单,但是如果我想要更复杂的东西的交集/消除怎么办?

Example, trying to get a collection of ClassA objects which is the result of the intersect on ClassA object's AStr1 and ClassB object's BStr ; 例如,尝试获取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);
    }
}

How can I fix the noted line to achieve this intersection. 如何修复注意到的线以实现此交集。

MoreLINQ has ExceptBy . MoreLINQExceptBy It doesn't have IntersectBy yet, but you could easily write your own implementation, and possibly even contribute it to MoreLINQ afterwards :) 它还没有IntersectBy ,但你可以轻松编写自己的实现,甚至可能在之后将它贡献给MoreLINQ :)

It would probably look something like this (omitting error checking): 或许,这将是这个样子(省略错误检查):

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;
        }
    }
}

If you wanted to perform an intersection on two completely different types which happened to have a common property type, you could make a more general method with three type parameters (one for first , one for second , and one for the common key type). 如果你想在两个完全不同的类型上执行一个碰巧有一个共同属性类型的交集,你可以使用三个类型参数(一个用于first ,一个用于second ,一个用于公共密钥类型)制作更通用的方法。

x ∈ A ∩ B if and only if x ∈ A and x ∈ B. x∈A∩B当且仅当x∈A且x∈B时。

So, for each a in aObj , you can check if a.AStr1 is in the set of BStr values. 因此,对于aObj每个a ,您可以检查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));
}

this code: 这段代码:

    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);
    }

has passed the following test: 已通过以下测试:

    [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"));
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM