LINQ查询中的交集

54

如果我有一个IEnumerable对象,其中ClassA公开了一个类型为long的ID属性。 是否可以使用Linq查询获取所有ID属于第二个IEnumerable对象的ClassA实例?

换句话说,这可行吗?

IEnumerable<ClassA> = original.Intersect(idsToFind....)?

original 是一个 IEnumerable<ClassA> 类型的对象,idsToFind 是一个 IEnumerable<long> 类型的对象。

7个回答

63

是的。

正如其他人已经回答的那样,你可以使用Where,但对于大型数据集来说,它将非常低效。

如果性能是一个问题,你可以调用Join

var results = original.Join(idsToFind, o => o.Id, id => id, (o, id) => o);

如果idsToFind可能包含重复项,则需要在ID上调用Distinct()或结果上调用Distinct(),或将Join替换为GroupJoin(GroupJoin的参数相同)。

这正是我在寻找的,谢谢。不知何故,这没有出现在我的原始问题中,但idsToFind = IEnumerable<long>。再次感谢。 - Klaus Nji
1
你有没有想过在 originalidsToFind 的哪个大小时,使用 JoinWhere/Contains 更有效率?我可以想象对于小列表(也许20-30项),Join 会有太多的开销。 - Tobias
为了帮助您,您可以使用依赖于“Join”的扩展方法:https://dev59.com/KnE95IYBdhLWcg3wRL0t#59321033 - aloisdg

20

我将使用Intersect发布一个答案。

如果您想要对相同类型的2个IEnumerables进行交集操作,则这非常有用。

首先,我们需要一个EqualityComparer

    public class KeyEqualityComparer<T> : IEqualityComparer<T>
    {
        private readonly Func<T, object> keyExtractor;

        public KeyEqualityComparer(Func<T, object> keyExtractor)
        {
            this.keyExtractor = keyExtractor;
        }

        public bool Equals(T x, T y)
        {
            return this.keyExtractor(x).Equals(this.keyExtractor(y));
        }

        public int GetHashCode(T obj)
        {
            return this.keyExtractor(obj).GetHashCode();
        }
    }

其次,我们将KeyEqualityComparer应用于Intersect函数:
var list3= list1.Intersect(list2, new KeyEqualityComparer<ClassToCompare>(s => s.Id));

12
你可以这样做,但在当前形式下,你需要使用Where扩展方法。
var results = original.Where(x => yourEnumerable.Contains(x.ID));

Intersect方法可以找到两个 IEnumerable 中共同存在的元素。如果你只是想要一个ID列表,你可以采用下面这种方式,利用Intersect方法:

var ids = original.Select(x => x.ID).Intersect(yourEnumerable);

6
一个简单的方法是:
IEnumerable<ClassA> result = original.Where(a => idsToFind.contains(a.ID));

2
使用Where方法来过滤结果:
var result = original.Where(o => idsToFind.Contains(o.ID));

1
命名很重要。这里是一个基于Join操作符的扩展方法:
private static IEnumerable<TSource> IntersectBy<TSource, TKey>(
    this IEnumerable<TSource> source,
    IEnumerable<TKey> keys,
    Func<TSource, TKey> keySelector)
        => source.Join(keys, keySelector, id => id, (o, id) => o);

你可以像这样使用它:var result = items.IntersectBy(ids, item => item.id)

0
早上我一直在研究Intersect,它在核心3中不再工作了,因为它是客户端而不是服务器端。从数据库中获取一组项目列表后,用户可以选择以需要子项附加到原始列表的方式显示它们,以获取更多信息。曾经起作用的是:
itemList = _context.Item
        .Intersect(itemList)
        .Include(i => i.Notes)
        .ToList();

现在似乎有效的是:

itemList = _context.Item
        .Where(item => itemList.Contains(item))
        .Include(i => i.Notes)
        .ToList();

这似乎按预期工作,没有任何显著的性能差异,并且与第一个一样简单。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接