Is there an IDictionary implementation that, on missing key, returns the default value instead of throwing?

27,816

Solution 1

Indeed, that won't be efficient at all.

As per comments, in .Net Core 2+ / NetStandard 2.1+ / Net 5, MS added the extension method GetValueOrDefault()

For earlier versions you can write the extension method yourself:

public static TValue GetValueOrDefault<TKey,TValue>
    (this IDictionary<TKey, TValue> dictionary, TKey key)
{
    TValue ret;
    // Ignore return value
    dictionary.TryGetValue(key, out ret);
    return ret;
}

Or with C# 7.1:

public static TValue GetValueOrDefault<TKey,TValue>
    (this IDictionary<TKey, TValue> dictionary, TKey key) =>
    dictionary.TryGetValue(key, out var ret) ? ret : default;

That uses:

  • An expression-bodied method (C# 6)
  • An out variable (C# 7.0)
  • A default literal (C# 7.1)

Solution 2

If you're using .NET Core 2 or above (C# 7.x), the CollectionExtensions class is introduced and you can use the GetValueOrDefault method to get default value if key is not there in a dictionary.

Dictionary<string, string> colorData = new Dictionary<string, string>();
string color = colorData.GetValueOrDefault("colorId", string.Empty);

Solution 3

Carrying these extension methods can help..

public static V GetValueOrDefault<K, V>(this IDictionary<K, V> dict, K key)
{
    return dict.GetValueOrDefault(key, default(V));
}

public static V GetValueOrDefault<K, V>(this IDictionary<K, V> dict, K key, V defVal)
{
    return dict.GetValueOrDefault(key, () => defVal);
}

public static V GetValueOrDefault<K, V>(this IDictionary<K, V> dict, K key, Func<V> defValSelector)
{
    V value;
    return dict.TryGetValue(key, out value) ? value : defValSelector();
}

Solution 4

Collections.Specialized.StringDictionary provides a non-exception result when looking up a missing key's value. It is also case-insensitive by default.

Caveats

It is only valid for its specialized uses, and — being designed before generics — it doesn't have a very good enumerator if you need to review the whole collection.

Solution 5

If you're using .Net Core, you can use the CollectionExtensions.GetValueOrDefault method. This is the same as the implementation provided in the accepted answer.

public static TValue GetValueOrDefault<TKey,TValue> (
   this System.Collections.Generic.IReadOnlyDictionary<TKey,TValue> dictionary,
   TKey key);
Share:
27,816

Related videos on Youtube

TheSoftwareJedi
Author by

TheSoftwareJedi

I write C# code

Updated on September 15, 2021

Comments

  • TheSoftwareJedi
    TheSoftwareJedi over 2 years

    The indexer into Dictionary throws an exception if the key is missing. Is there an implementation of IDictionary that instead will return default(T)?

    I know about the TryGetValue() method, but that's impossible to use with LINQ.

    Would this efficiently do what I need?:

    myDict.FirstOrDefault(a => a.Key == someKeyKalue);
    

    I don't think it will as I think it will iterate the keys instead of using a Hash lookup.

    • Rory O'Kane
      Rory O'Kane about 11 years
      See also this later question, marked as a duplicate of this one, but with different answers: Dictionary returning a default value if the key does not exist
    • TheSoftwareJedi
      TheSoftwareJedi over 6 years
      @dylan That will throw an exception on missing key, not null. In addition it would require deciding what the default should be everywhere the dictionary is used. Also take note of the age of this question. We didn’t have the ?? operator 8 years ago anyhow
  • Jon Skeet
    Jon Skeet over 15 years
    It could be - but in some situations you may well know that it's not. I can see this being useful sometimes.
  • TheSoftwareJedi
    TheSoftwareJedi over 15 years
    If you know null isn't in there - this is extremely nice to have. It makes joining these things in Linq (which is all I do lately) so much easier
  • MattDavey
    MattDavey over 12 years
    By using the ContainsKey method?
  • Peter Gluck
    Peter Gluck over 11 years
    Or more compactly: return (dictionary.ContainsKey(key)) ? dictionary[key] : default(TValue);
  • Jon Skeet
    Jon Skeet over 11 years
    @PeterGluck: More compactly, but less efficiently... why perform two look-ups in the case when the key is present?
  • J Bryan Price
    J Bryan Price over 11 years
    @JonSkeet: Thanks for correcting Peter; I've been using that "less efficient" method without realizing it for a while now.
  • MEMark
    MEMark about 10 years
    The last overload is interesting. Since there is nothing to select from, is this simply a form of lazy evaluation?
  • nawfal
    nawfal about 10 years
    @MEMark yes value selector is run only if needed, so in a way it can be said as lazy evaluation, but its not deferred execution as in Linq context. But lazy evaluation here is not dependent on nothing to select from factor, you can of course make the selector Func<K, V>.
  • Jon Coombs
    Jon Coombs about 10 years
    Yes, it's actually very useful. Python has this and I use it far more than the plain method, when I happen to be in Python. Saves writing a lot of extra if statements that are just checking for null result. (You're right, of course, that null is occasionally useful, but usually I'm wanting a "" or 0 instead.)
  • Jon Coombs
    Jon Coombs about 10 years
    Very nice! I'm going to shamelessly plagiarize--er, fork it. ;)
  • Jon Coombs
    Jon Coombs about 10 years
    Is that third method public because it's useful in its own right? That is, are you thinking someone might pass in a lambda that uses the current time, or a calculation based on... hmm. I would have expected to just have the second method do V value; return dict.TryGetValue(key, out value) ? value : defVal;
  • nawfal
    nawfal about 10 years
    @JCoombs right, the third overload is there for the same purpose. And ya of course you can do that in second overload, but I was making it a bit dry (so that I dont repeat logic).
  • Jon Coombs
    Jon Coombs about 10 years
    @nawfal Good point. Staying dry is more important than avoiding one measly method call.
  • nawfal
    nawfal almost 10 years
    I had upvoted this. But if it was today I wouldnt have. Cant cancel now :). I upvoted the comments to make my point :)
  • piedar
    piedar over 7 years
    This has two lookups instead of one and it introduces a race condition if you're using ConcurrentDictionary.
  • jjaskulowski
    jjaskulowski over 7 years
    This is insignificant for this question. Besides, in many languages this is how standard types work and all is fine (ex. R)
  • theMayer
    theMayer about 5 years
    Apparently MS decided this was good enough to add to System.Collections.Generic.CollectionExtensions, as I just tried and it's there.
  • ianmac45
    ianmac45 over 4 years
    FYI, @theMayer 's answer is only in .NET Core 2.0+, not the full Framework. But, still, a good find.
  • Per Lundberg
    Per Lundberg over 3 years
    For reference, the .NET 5 MSDN docs for this method is here: docs.microsoft.com/en-us/dotnet/api/…. There is also another convenient overload which lets you override the default value returned.
  • mkb
    mkb over 3 years
    so we can use like this: myDict[key]?.Name??"" ? a usage sample would be great