Using LINQ to objects Intersect and Except on a specific property

风格不统一 提交于 2019-12-05 06:12:22
Jon Skeet

MoreLINQ has ExceptBy. It doesn't have IntersectBy yet, but you could easily write your own implementation, and possibly even contribute it to MoreLINQ afterwards :)

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).

x ∈ A ∩ B if and only if x ∈ A and x ∈ B.

So, for each a in aObj, you can check if a.AStr1 is in the set of BStr values.

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"));
    }
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!