AnonyMouse
AnonyMouse

Reputation: 18630

How to select items from two lists where the ids don't match

Ok, so I have two lists of items which are of different types.

var whales = new List<Whale>();

var crabs = new List<Crab>();

So they both have the id property. So save the lists have objects with Ids of:

whales: 1, 3, 4, 5 crabs: 1, 2, 3, 4

Ok so I have a query:

var matchedPairs = from c in crabs
                   from w in whales
                   where c.Id = w.Id
                   select new { crab = c, whale = w };

So that works fine for getting the matches. Where I'm having trouble is I want to get a list of crabs that don't have a matching whale ie. Crab Id = 2. Then I want to get the whales that don't have a matching crab ie Whale Id = 5.

Can anyone tell me how to write these queries?

Upvotes: 4

Views: 8302

Answers (7)

Glen Hughes
Glen Hughes

Reputation: 4812

Maybe something like this:

var unmatchedCrabs = from c in crabs
                     where !whales.Any(w => w.Id == c.Id)
                     select c;

Upvotes: 4

Blindsniper
Blindsniper

Reputation: 56

You need two Left Join by GroupJoin method like this:

var result1 = whales.GroupJoin(crabs, w => w.ID, c => c.ID, (w,cs) => new {WhaleID = w.ID, Matches = cs});
var result2 = crabs.GroupJoin(whales, c => c.ID, w => w.ID, (c, ws) => new {CrabID = c.ID, Matches = ws});

Then, filter the result by what you want.

Upvotes: 0

Enigmativity
Enigmativity

Reputation: 117084

Here's the neatest looking LINQ that I can think of to do what you need:

var whalesOnly =
    from w in whales
    join c in crabs on w.Id equals c.Id into gcs
    where !gcs.Any()
    select w;

var crabsOnly =
    from c in crabs
    join w in whales on c.Id equals w.Id into gws
    where !gws.Any()
    select c;

How do these look for you?

BTW, you can do your join query a little nicer like this:

var whalesAndCrabs =
    from whale in whales
    join crab in crabs on whale.Id equals crab.Id
    select new { crab, whale };

Upvotes: 0

Andrew Kennan
Andrew Kennan

Reputation: 14157

You want an outer join:

var crabsWithoutWhales = from c in crabs
                         join w1 in whales on c.Id equals w1.Id into ws
                         from w2 in ws.DefaultIfEmpty()
                         where w2 == null
                         select c;

Upvotes: 2

Dmitry Khryukin
Dmitry Khryukin

Reputation: 6438

var result = crabs.SelectMany(c => whales, (c, w) => new { c, w })
                              .Where(@t => whales.All(x => x.Id != t.c.Id) && crabs.All(x => x.Id != t.w.Id))
                              .Select(@t => new {crab = @t.c, whale = @t.w});

Upvotes: 0

Dmitry Khryukin
Dmitry Khryukin

Reputation: 6438

if you want to select only crabs.

var result = crabs.Where(c => whales.All(w => w.Id != c.Id));

Upvotes: 12

loopedcode
loopedcode

Reputation: 4893

You can use Union operation from set operations.

To use it you will have to override default Equality comparer and GetHashCode method. Once you have those then you can do something like this:

var matchedPair = crabs.Union(whales);

In your case, you should have a base class; e.g. Animals with Equality comparer. Another option would be to implement IEqualityComparer<>

Upvotes: 1

Related Questions