Mason Wheeler
Mason Wheeler

Reputation: 84650

How to produce a fully unique lookup?

Let's say I have an IEnumerable of key/value pairs, that contains duplicates. If I call ToLookup on it and pass lambdas that select the key and the value, I'll end up with a Lookup where all the keys are unique, but the lists of values can contain duplicates. Is there any good way to end up with a Lookup where none of the lists of values contain duplicates, in a single LINQ query statement?

I can't call Distinct on the original sequence it because the elements are duplicates by value but not by object identity, and Distinct doesn't take a lambda for a selector that I could use.

Upvotes: 3

Views: 1264

Answers (3)

Enigmativity
Enigmativity

Reputation: 117154

It seems rather like a trivial exercise if you introduce an anonymous intermediate object.

Try this:

var lookup =
    sequence
        .Select(x => new { x.Key, x.Value })
        .Distinct()
        .ToLookup(x => x.Key, x => x.Value);

Since you can call .Distinct() on anonymous variables then this should work just fine.

Upvotes: 0

Robert S.
Robert S.

Reputation: 2042

You can use a little GroupBy hack instead of Distinct:

var lookup = source.GroupBy(i => get_unique_id(i),
    (key, group) => group.First()).ToLookup(i => i.Key, i => i.Value);

GroupBy will create groups based on the "unique value" you choose. The call to group.First will only keep one element of that group. So you remove all others with the same "unique value" or "group criteria". This is basically an extended Distinct.

get_unique_id should return the same value for the same key/value combination and a different value otherwise.

Upvotes: 1

CSharpie
CSharpie

Reputation: 9477

You can use a GroupBy before building the Lookup in Combination with a select many. This doesnt feel so good however:

Something like this:

sequence.GroupBy(kvp => _getKey(kvp))
        .SelectMany(grp => grp.Distinct()
                              .Select(value => new { grp.Key, Value = value}))
        .ToLookUp(grp => grp.Key, grp=> grp.Value);

Alternative would be using a Dictionary, since I am not quite sure why you want to use lookup.

sequence.GroupBy(kvp => _getKey(kvp))
        .Select(g => new { g.Key, Values = new HashSet<WhatEver>(g)})
        .ToDictionary(v => v.Key, v => v.Values);

Alternative to Hashset would be providing an EqualityComparer with the Distinct()

 public sealed class WhatEverComparer : IEqualityComparer<WhatEver>
 {
    ... implement interface..
 }

Then

sequence.GroupBy(kvp => _getKey(kvp))
        .Select(g => new { g.Key, Values = g.Distinct(new WhatEvercomparer()).ToArray()})
        .ToDictionary(v => v.Key, v => v.Values);

Upvotes: 1

Related Questions