tags:

views:

65

answers:

4

If I have an enumeration of dictionaries

IEnumerable<IDictionary<string, float>> enumeration

can I perform a Linq query on it so that I can select by a value from each dictionary in the enumeration using the same key?

I can do this in a loop:

float f;
foreach (var dictionary in enumeration)
{
    if (dictionary.TryGetValue("some key", out f))
    {
        Console.WriteLine(f);
    }
}

(The eventual plan is to compare the performance of the query verses the equivalent nested looping statements (the enumeration itself is formed from either another query or an equivalent set of loops).)

+4  A: 

Are you looking for something like this:

IEnumerable<float> vals = enumeration.Where( d => d.ContainsKey( "some key" ) )
                                     .Select( d => d["some key"] );

This query first identifies which dictionaries in the sequence contain the specified key, and then for each of those retrieves the value for the given key.

This is not as efficient as a loop which uses TryGetValue(), because it will perform two dictionary accesses - one for the Where and another for the Select. Alternatively, you could create a safe method that returns a value or a default from the dictionary, and then filter out the defaults. This eliminates the duplicate dictionary lookup.

public static class DictionaryExt {
    public static TValue FindOrDefault<TKey,TValue>( 
            this Dictionary<TKey,TValue> dic,
            TKey key, TValue defaultValue )  
    {
        TValue val;
        return dic.TryGetValue( key, out val ) ? val : defaultValue;
    }
}

enumeration.Select( d => d.FindOrDefault( "some key", float.NaN ) )
           .Where ( f => f != float.NaN );
LBushkin
+2  A: 

LINQ over objects just uses normal .NET methods, so you probably won't notice any performance difference - LINQ may be a tiny bit worse if anything because of a tiny bit of overhead, but I wouldn't expect it to be noticeable.

Maybe something like:

var q = from d in enumeration
        where d.ContainsKey("some key")
        select d["some key"];

foreach (float f in q)
{
    Console.WriteLine(f);
}
Joe Enos
+1  A: 

Using TryGetValue:

float f = 0.0f;
foreach (var dic in enumeration.Where(d => d.TryGetValue("some key", out f))) {
  Console.WriteLine(f);
}
Julien Lebosquain
A: 

If the query is run frequently on an unchanging set of dictionaries... just capture the results to a Lookup instance.

 //run once
ILookup<string, float> myLookup = enumeration
  .SelectMany(d => d)
  .ToLookup(kvp => kvp.Key, kvp => kvp.Value);

 //run many times
foreach(float f in myLookup["somekey"])
{
  Console.WriteLine(f);
}

It's important to note that if the key is not present in the lookup, you'll get an empty IEnumerable<float> (but not a null).

David B