Reputation: 1660
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
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