Alexander Galkin
Alexander Galkin

Reputation: 12524

LINQ: Getting Keys for a given list of Values from Dictionary and vice versa

I have the following structure in my code Dictionary<TKeys, TValues> data;. I run some LINQ queries on both data types and often need to switch between Keys and Values. What is the best way to get the list of Keys for given Values and vice versa? Please, notice, that I usually have 'IEnumerable' and 'IEnumerable' as a result of my previous LINQ queries and would like to have something like IEnumerable<TKeys> Dictionary.GetAllKeys(IEnumerable<IValues> vals) and IEnumerable<TValues> Dictionary.GetAllValues(IEnumerable<IKeys> keys).

Maybe I need other data container for this task?

Regards, Alexander.

Upvotes: 24

Views: 87484

Answers (3)

Jon Skeet
Jon Skeet

Reputation: 1499860

A Dictionary<,> really isn't great for finding keys by value. You could write a bidirectional dictionary, as I have done in this answer, but it wouldn't necessarily be the best approach.

Of course you can use a dictionary as a sequence of key/value pairs, so you could have:

var keysForValues = dictionary.Where(pair => values.Contains(pair.Value))
                              .Select(pair => pair.Key);

Just be aware this will be an O(n) operation, even if your "values" is a HashSet or something similar (with an efficient containment check).

EDIT: If you don't really need a key/value relation - if it's more like they're just pairs - then using List<Tuple<Foo, Bar>> would make a certain amount of sense. The query ends up being the same, basically:

public IEnumerable<T1> GetAllFirst<T1, T2>(IEnumerable<Tuple<T1, T2>> source,
                                           IEnumerable<T2> seconds)
{
    HashSet<T2> secondsSet = new HashSet<T2>(seconds);
    return source.Where(pair => secondsSet.Contains(pair.Item2));
}

public IEnumerable<T2> GetAllSecond<T1, T2>(IEnumerable<Tuple<T1, T2>> source,
                                            IEnumerable<T1> firsts)
{
    HashSet<T1> firstsSet = new HashSet<T1>(firsts);
    return source.Where(pair => firstsSet.Contains(pair.Item1));
}

Upvotes: 11

ColinE
ColinE

Reputation: 70122

The best approach is to perform your linq query on the collection of key-value pairs, then use a Select projection to select either the Keys or the Values at the end of your query. This way there is no need to perform a look-up at the end of your query.

For example:

  Dictionary<string, string> data = new Dictionary<string, string>();
  // select all values for keys that contain the letter 'A'
  var values = data.Where(pair => pair.Key.Contains("A"))
                   .Select(pair => pair.Value);

Upvotes: 2

Bala R
Bala R

Reputation: 108937

 var values = dictionary.Where(x => someKeys.Contains(x.Key)).Select(x => x.Value);
 var keys = dictionary.Where(x => someValues.Contains(x.Value)).Select(x => x.Key);

Upvotes: 47

Related Questions