Klaus Nji
Klaus Nji

Reputation: 18857

Intersect LINQ query

If I have an IEnumerable where ClassA exposes an ID property of type long. Is it possible to use a Linq query to get all instances of ClassA with ID belonging to a second IEnumerable?

In other words, can this be done?

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

where original is an IEnumerable<ClassA> and idsToFind is IEnumerable<long>.

Upvotes: 55

Views: 105111

Answers (7)

Dan
Dan

Reputation: 61

I've been tripping up all morning on Intersect, and how it doesn't work anymore in core 3, due to it being client side not server side.

From a list of items pulled from a database, the user can then choose to display them in a way that requires children to attached to that original list to get more information.

What use to work was:

itemList = _context.Item
        .Intersect(itemList)
        .Include(i => i.Notes)
        .ToList();

What seems to now work is:

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

This seems to be working as expected, without any significant performance difference, and is really no more complicated than the first.

Upvotes: 0

aloisdg
aloisdg

Reputation: 23521

Naming things is important. Here is an extension method base on the Join operator:

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

You can use it like this var result = items.IntersectBy(ids, item => item.id).

Upvotes: 1

Dragos Durlut
Dragos Durlut

Reputation: 8098

I will post an answer using Intersect.

This is useful if you want to intersect 2 IEnumerables of the same type.

First we will need an 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();
        }
    }

Secondly we apply the KeyEqualityComparer to the Intersect function:

var list3= list1.Intersect(list2, new KeyEqualityComparer<ClassToCompare>(s => s.Id));

Upvotes: 20

SLaks
SLaks

Reputation: 887385

Yes.

As other people have answered, you can use Where, but it will be extremely inefficient for large sets.

If performance is a concern, you can call Join:

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

If idsToFind can contain duplicates, you'll need to either call Distinct() on the IDs or on the results or replace Join with GroupJoin (The parameters to GroupJoin would be the same).

Upvotes: 64

bruno conde
bruno conde

Reputation: 48265

A simple way would be:

IEnumerable<ClassA> result = original.Where(a => idsToFind.contains(a.ID));

Upvotes: 6

David Pfeffer
David Pfeffer

Reputation: 39833

You can do it, but in the current form, you'd want to use the Where extension method.

var results = original.Where(x => yourEnumerable.Contains(x.ID));

Intersect on the other hand will find elements that are in both IEnumerable's. If you are looking for just a list of ID's, you can do the following which takes advantage of Intersect

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

Upvotes: 12

Ahmad Mageed
Ahmad Mageed

Reputation: 96477

Use the Where method to filter the results:

var result = original.Where(o => idsToFind.Contains(o.ID));

Upvotes: 2

Related Questions