Johan Pettersson
Johan Pettersson

Reputation: 29

Dictionary Values as distinct Keys

I have this:

        var color = new Dictionary<string, List<string>>();
        color.Add("Blue", new List<string>() { "1", "2" });
        color.Add("Green", new List<string>() { "2", "3" });
        color.Add("Red", new List<string>() { "1", "3" });
        color.Add("Black", new List<string>() { "3" });
        color.Add("Yellow", new List<string>() { "1" });

I would to convert this to a

Dictionary<string, List<string>>

that looks like this:

Key: 1, Value: Blue, Red, Yellow
Key: 2, Value: Blue, Green
Key: 3, Value: Green, Red, Black

Tried:

var test = color.GroupBy(x => x.Value.FirstOrDefault())
.ToDictionary(g => g.Key, g => g.Select(x => x.Key).ToList());

However this only works for one item so ("FirstOrDefault()"), i only get

Key: 1, Value: Blue, Red, Yellow

How could i do this? I could loop through the values (distinct) and there loop through the keys and check if value exist then build up a new one but i would like to avoid this and use lamda.

Seen many example of this but with no list of string as values but only a string.

Upvotes: 1

Views: 501

Answers (3)

Olivier Jacot-Descombes
Olivier Jacot-Descombes

Reputation: 112772

Basically, I had the same idea as @SamIAm and @kanders84152. I just want to make a little improvement and to add an explanation:

var test = color
    .SelectMany(c => c.Value.Select(v => new { Number = v, Color = c.Key }))
    .GroupBy(a => a.Number, a => a.Color)
    .ToDictionary(g => g.Key, g => g.ToList());

The main difference of my solution is that the GroupBy part extracts the color from the anonymous type instead of the ToDictionary part.

First the .SelectMany(c => c.Value.Select(v => new { Number = v, Color = c.Key })) part flattens the nested lists to an enumeration of an anonymous type with numbers and color names.

The original dictionary entries

"Blue", { "1", "2" }
"Green", { "2", "3" }
"Red", { "1", "3" }
"Black", { "3" }
"Yellow", { "1" }

become

{ Number: "1", Color: "Blue" }
{ Number: "2", Color: "Blue" }
{ Number: "2", Color: "Green" }
{ Number: "3", Color: "Green" }
{ Number: "1", Color: "Red" }
{ Number: "3", Color: "Red" }
{ Number: "3", Color: "Black" }
{ Number: "1", Color: "Yellow" }

.GroupBy(a => a.Number, a => a.Color) then re-groups these values by the number and takes the color as value

"1", { "Blue", "Red", "Yellow" }
"2", { "Blue", "Green" }
"3", { "Green" "Red", "Black" }

This is almost the desired result, except that we have an IGrouping<string, string> (where the groups represent a key and an enumeration of values) instead of a Dictionary<string, List<string>>. Finally .ToDictionary(g => g.Key, g => g.ToList()) creates the desired dictionary with the same structure.

Upvotes: 3

If I were you I'd flatten it first

var colorflat = color
    .SelectMany(c => c.Value.Select(n => new {Color=c.Key, Number=n}));

and then I'd group to the new format

var test = colorflat
    .GroupBy(c => c.Number)
    .ToDictionary(c => c.Key, c => c.Select(x => x.Color).ToList());

Upvotes: 0

kanders84152
kanders84152

Reputation: 1341

Try this:

var test = color
    .SelectMany(x => x.Value.Select(v =>
    new
    {
       Color = x.Key,
       Integer = v
    }))
    .GroupBy(x => x.Integer)
    .ToDictionary(x => x.Key, x => x.Select(y => y.Color).ToList());

Upvotes: 6

Related Questions