David Rubin
David Rubin

Reputation: 1660

How can I use LINQ to avoid calling Contains() inside a Where() clause?

I've got a LINQ expression that's pretty straightforward:

class Foo
{
   ...
   int Id { get; }
}

IEnumerable<Foo> foos = ...;
IEnumerable<int> ids = ...;

var remainder = foos.Where(f => !ids.Contains(f.Id));

I'm thinking that there might be a way to produce the same remainder set using Join() and/or Intersection() and/or Except(), but I'm not clever enough to formulate it.

I'm struggling with the ! in the Join(), because projected-set intersection in terms of Join() is:

var intersection = foos.Join(ids, f => f.Id, i => i, (f, id) => f);

but I'm looking for the inverse. Can you help? Thanks!

Upvotes: 1

Views: 1658

Answers (1)

Ivan Stoev
Ivan Stoev

Reputation: 205889

What you are seeking for in the Relational algrebra is called Antijoin. In relational database it's implemented usually with left outer join (or left semijoin if we use the same terminology) with checking right side for NULL. The closest LINQ construct is the GroupJoin with check for empty inner group.

Here is how it will look for your sample (compare it to Contains based version)

Query syntax:

var remainder = from foo in foos
        join id in ids on foo.Id equals id into idGroup
        where !idGroup.Any()
        select foo;

Method syntax:

var remainder = foos.GroupJoin(ids, foo => foo.Id, id => id, (foo, idGroup) => new { foo, idGroup })
    .Where(e => !e.idGroup.Any())
    .Select(e => e.foo);

Upvotes: 6

Related Questions