【问题标题】:Using LINQ to objects Intersect and Except on a specific property使用 LINQ 对特定属性的对象相交和除外
【发布时间】:2010-10-22 15:14:24
【问题描述】:

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

例如,试图获取ClassA 对象的AStr1ClassB 对象的BStr 相交的结果的ClassA 对象集合; :

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

我怎样才能修复注意到的线来实现这个交叉点。

【问题讨论】:

    标签: c# linq-to-objects intersect


    【解决方案1】:

    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,一个用于second,一个用于用于公共密钥类型)。

    【讨论】:

    • 嗨@Jon Skeet,我不明白如何传递第四个参数。我是不是通过实现 IEqualityComparer 来定义一个类,然后传递这个类的一个实例?
    • @corei11:是的,如果您需要自定义的东西 - 如果您对默认的相等操作感到满意,请使用 EqualityComparer&lt;T&gt;.Default
    • 谢谢@Jon Skeet。这里我有两个IEnumerable owner1owner2。我通过实现IEqualityComparer 定义了一个名为Compar 的类。现在通过这种方式调用方法owner1 = owner1.IntersectBy(owner2, item =&gt; item.Email_Address, new Compar&lt;string&gt;())。实际上,我想弄清楚如何通过我的 Compar 课程。那是直接我写字符串还是有其他更好的方法或者我的方法可以吗?我的比较类原型是public class Compar&lt;Tkey&gt; : IEqualityComparer&lt;Tkey&gt;。对不起。这可能是一个愚蠢的问题。谢谢。
    • @corei11:在这一点上,我认为您应该使用minimal reproducible example 提出一个新问题。
    【解决方案2】:

    x ∈ A ∩ B 当且仅当 x ∈ A 且 x ∈ B.

    因此,对于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));
    }
    

    【讨论】:

      【解决方案3】:

      这段代码:

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

      【讨论】:

        猜你喜欢
        • 2012-05-24
        • 2012-07-19
        • 2016-07-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-07-22
        相关资源
        最近更新 更多