[英]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>
对象时,我可以直接使用Intersect
和Except
来获得输出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
对象的AStr1
和ClassB
对象的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
. MoreLINQ有
ExceptBy
。 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.