agenthost
agenthost

Reputation: 768

How to find the key of a dictionary when the value is a list of objects in C#?

I want to find the corresponding key when I have a list of objects as a value. Suppose I have a Dictionary,

 Dictionary<string, List<object>> dict = new Dictionary<string, List<object>>(); 

I know I could do something like,

foreach (var item in dict)
{
    foreach (var subItem in item.Value)
    {
        if (subItem.Equals(foo))
        {
              Console.WriteLine(item.Key);
        }
     }
}

But this takes a lot of time I have a huge data set. Is there a faster solution to this?

I know that the normal way of finding keys given the value, using LINQ is something like:

var keysWithMatchingValues = dict.Where(p => p.Value == myObject).Select(p => p.Key);

I am looking for a similar solution in my situation.

Upvotes: 2

Views: 1530

Answers (1)

Tim Schmelter
Tim Schmelter

Reputation: 460288

LINQ isn't more performant most times, but it's often easier to write correct and readable code with LINQ. If no duplicate values are possible i'd use a HashSet<T> instead of the list. If you know what type it is i'd use that type instead of object.

The LINQ version of your code would be....

  1. with List.Contains:

    List<string> keysWithValue =  dict
        .Where(kv => kv.Value.Contains(foo))
        .Select(kv => kv.Key);
        .ToList();
    
  2. or Enumerable.Any:

    List<string> keysWithValue =  dict
        .Where(kv => kv.Value.Any(v => foo.Equals(v)))
        .Select(kv => kv.Key);
        .ToList();
    

But as mentioned this wouldn't be more efficient. One way to improve performance would be to use a Lookup<TKey, TValue>. If the dictionary doesn't change you only need to create it once:

var valueToKeyLookup = dict  // make it an instance field, so that you don't have to create it always
    .SelectMany(kv => kv.Value
        .Distinct()
        .Select(v => new {Key = kv.Key, Value = v})
    )
    .ToLookup(x => x.Value, x => x.Key);

Now the remaining code is very concise and efficient:

List<string> allKeysWithFoo = valueToKeyLookup[foo].ToList();

Note that this will even work if no list contains that value, then the result will be an empty list.

Upvotes: 3

Related Questions