简体   繁体   中英

Getting TKey from a dictionary with common values where TValue is List<string>

I have a dictionary which looks like this:

Dictionary<string, List<string>> dict = new Dictionary<string, List<string>>()
{
    {"a" , new List<string> { "Red","Yellow"} },
    {"b" , new List<string> { "Blue","Red"} },
    {"c" , new List<string> { "Green","Orange"} },
    {"d" , new List<string> { "Black","Green"} },
};

I need output as a dictionary from the dict where common value in the List<string> should be the key and the value should be the list of keys.

Eg:

Red: [a,b]
Green: [c,d]

I don't know how to solve this problem with list in dictionary as TValue .

Please explain me how to handle lists with in dictionary.

You can flattern your dictionary with SelectMany and get plain list that looks like

"a" - "Red"
"a" - "Yellow"
"b" - "Blue"
"b" = "Red"
// and so on

then group by value and build a new dictionary from those groups. Try this code:

var commonValues = dict.SelectMany(kv => kv.Value.Select(v => new {key = kv.Key, value = v}))
    .GroupBy(x => x.value)
    .Where(g => g.Count() > 1)
    .ToDictionary(g => g.Key, g => g.Select(x => x.key).ToList());

Lots of looping... Loop over the dictionary, then loop over each value in the list.

var result = new Dictionary<string, List<string>>();

// Loop through each key/value pair in the dictionary
foreach (var kvp in dict)
{
    // kvp.Key is the key ("a", "b", etc)
    // kvp.Value is the list of values ("Red", "Yellow", etc)

    // Loop through each of the values
    foreach (var value in kvp.Value)
    {
        // See if our results dictionary already has an entry for this
        // value. If so, grab the corresponding list of keys. If not,
        // create a new list of keys and insert it.
        if (!result.TryGetValue(value, out var list))
        {
            list = new List<string>();
            result.Add(value, list);
        }

        // Add our key to this list of keys
        list.Add(kvp.Key);
    }
}

If you want to filter this by entries which have more than one item, then you can do:

result = result.Where(x => x.Value.Count > 1).ToDictionary(x => x.Key, x => x.Value);

Alternatively, you can avoid loops and use Linq instead:

// Flatten the dictionary into a set of tuples
// e.g. (a, Red), (a, Yellow), (b, Blue), (b, Red), etc
var result = dict.SelectMany(kvp => kvp.Value.Select(color => (key: kvp.Key, color)))
    // Group by the value, taking the color as the elements of the group
    // e.g. (Red, (a, b)), (Yellow, (a)), etc
    .GroupBy(item => item.color, item => item.key)
    // Filter to the ones with more than one item
    .Where(group => group.Count() > 1)
    // Turn it into a dictionary, taking the key of the grouping
    // (Red, Green, etc), as the dictionary key
    .ToDictionary(group => group.Key, group => group.ToList());

You can also use linq query syntax, which is slightly longer but avoids the mess around the SelectMany :

var result =
    (
        from kvp in dict
        from color in kvp.Value
        group kvp.Key by color into grp
        where grp.Count() > 1
        select grp
    ).ToDictionary(grp => grp.Key, grp => grp.ToList());

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM