164

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.

Massimiliano Kraus
  • 3,638
  • 5
  • 27
  • 47
TheSoftwareJedi
  • 34,421
  • 21
  • 109
  • 151
  • 10
    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](http://stackoverflow.com/q/2601477/578288) – Rory O'Kane Apr 30 '13 at 17:12
  • 2
    @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 – TheSoftwareJedi Oct 24 '17 at 16:06

17 Answers17

182

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)
Chris F Carroll
  • 11,146
  • 3
  • 53
  • 61
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 3
    Or more compactly: `return (dictionary.ContainsKey(key)) ? dictionary[key] : default(TValue);` – Peter Gluck Oct 09 '12 at 19:41
  • 41
    @PeterGluck: More compactly, but less efficiently... why perform two look-ups in the case when the key is present? – Jon Skeet Oct 09 '12 at 19:43
  • 6
    @JonSkeet: Thanks for correcting Peter; I've been using that "less efficient" method without realizing it for a while now. – J Bryan Price Jan 14 '13 at 22:20
  • 5
    Very nice! I'm going to shamelessly plagiarize--er, fork it. ;) – Jon Coombs Mar 24 '14 at 05:10
  • 6
    Apparently MS decided this was good enough to add to `System.Collections.Generic.CollectionExtensions`, as I just tried and it's there. – theMayer Feb 21 '19 at 19:43
  • 2
    FYI, @theMayer 's answer is only in .NET Core 2.0+, not the full Framework. But, still, a good find. – ianmac45 Oct 02 '19 at 15:34
  • For reference, the .NET 5 MSDN docs for this method is here: https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.collectionextensions.getvalueordefault?view=net-5.0#System_Collections_Generic_CollectionExtensions_GetValueOrDefault__2_System_Collections_Generic_IReadOnlyDictionary___0___1____0_. There is also another convenient overload which lets you override the default value returned. – Per Lundberg Nov 20 '20 at 21:05
  • What if you actually want this behavior on the interface member? Nothing but to roll your own DictionaryWithDefault? – Kyle McClellan Jul 07 '22 at 21:06
  • 1
    @KyleMcClellan: Yes. Although arguably then you're not *really* fulfilling the `IDictionary<,>` interface, which documents that `KeyNotFoundException` should be thrown. – Jon Skeet Jul 08 '22 at 06:39
29

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);
Pang
  • 9,564
  • 146
  • 81
  • 122
cdev
  • 5,043
  • 2
  • 33
  • 32
19

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();
}
nawfal
  • 70,104
  • 56
  • 326
  • 368
  • 3
    The last overload is interesting. Since there is nothing to select _from_, is this simply a form of lazy evaluation? – MEMark Mar 20 '14 at 08:58
  • 1
    @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`. – nawfal Mar 20 '14 at 12:12
  • 1
    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; – Jon Coombs Mar 24 '14 at 06:15
  • 1
    @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). – nawfal Mar 24 '14 at 12:36
  • 4
    @nawfal Good point. Staying dry is more important than avoiding one measly method call. – Jon Coombs Mar 26 '14 at 17:28
4

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);
theMayer
  • 15,456
  • 7
  • 58
  • 90
3

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.

jpaugh
  • 6,634
  • 4
  • 38
  • 90
Mark Hurd
  • 10,665
  • 10
  • 68
  • 101
3

Modern Answer

As of .NET Core 2.0, there is a built-in extension method with 2 overloads:

TValue GetValueOrDefault<TKey,TValue>(TKey)
TValue GetValueOrDefault<TKey,TValue>(TKey, TValue)

Usage:

var dict = new Dictionary<string, int>();

dict.GetValueOrDefault("foo");     // 0: the datatype's default
dict.GetValueOrDefault("foo", 2);  // 2: the specified default

The first version returns null for nullable types, of course.

See the documentation for more detail.

MarredCheese
  • 17,541
  • 8
  • 92
  • 91
2
public class DefaultIndexerDictionary<TKey, TValue> : IDictionary<TKey, TValue>
{
    private IDictionary<TKey, TValue> _dict = new Dictionary<TKey, TValue>();

    public TValue this[TKey key]
    {
        get
        {
            TValue val;
            if (!TryGetValue(key, out val))
                return default(TValue);
            return val;
        }

        set { _dict[key] = value; }
    }

    public ICollection<TKey> Keys => _dict.Keys;

    public ICollection<TValue> Values => _dict.Values;

    public int Count => _dict.Count;

    public bool IsReadOnly => _dict.IsReadOnly;

    public void Add(TKey key, TValue value)
    {
        _dict.Add(key, value);
    }

    public void Add(KeyValuePair<TKey, TValue> item)
    {
        _dict.Add(item);
    }

    public void Clear()
    {
        _dict.Clear();
    }

    public bool Contains(KeyValuePair<TKey, TValue> item)
    {
        return _dict.Contains(item);
    }

    public bool ContainsKey(TKey key)
    {
        return _dict.ContainsKey(key);
    }

    public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
    {
        _dict.CopyTo(array, arrayIndex);
    }

    public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
    {
        return _dict.GetEnumerator();
    }

    public bool Remove(TKey key)
    {
        return _dict.Remove(key);
    }

    public bool Remove(KeyValuePair<TKey, TValue> item)
    {
        return _dict.Remove(item);
    }

    public bool TryGetValue(TKey key, out TValue value)
    {
        return _dict.TryGetValue(key, out value);
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return _dict.GetEnumerator();
    }
}
Brian
  • 395
  • 1
  • 3
  • 14
  • so we can use like this: `myDict[key]?.Name??""` ? a usage sample would be great – mkb Dec 27 '20 at 03:59
1

If you are using ASP.NET MVC, you could leverage the RouteValueDictionary class that do the job.

public object this[string key]
{
  get
  {
    object obj;
    this.TryGetValue(key, out obj);
    return obj;
  }
  set
  {
    this._dictionary[key] = value;
  }
}
Jone Polvora
  • 2,184
  • 1
  • 22
  • 33
1

I used encapsulation to create an IDictionary with behavior very similar to an STL map, for those of you who are familiar with c++. For those who aren't:

  • indexer get {} in SafeDictionary below returns the default value if a key is not present, and adds that key to the dictionary with a default value. This is often the desired behavior, as you're looking up items that will appear eventually or have a good chance of appearing.
  • method Add(TK key, TV val) behaves as an AddOrUpdate method, replacing the value present if it exists instead of throwing. I don't see why m$ doesn't have an AddOrUpdate method and thinks throwing errors in very common scenarios is a good idea.

TL/DR - SafeDictionary is written so as to never throw exceptions under any circumstances, other than perverse scenarios, such as the computer being out of memory (or on fire). It does this by replacing Add with AddOrUpdate behavior and returning default instead of throwing NotFoundException from the indexer.

Here's the code:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

public class SafeDictionary<TK, TD>: IDictionary<TK, TD> {
    Dictionary<TK, TD> _underlying = new Dictionary<TK, TD>();
    public ICollection<TK> Keys => _underlying.Keys;
    public ICollection<TD> Values => _underlying.Values;
    public int Count => _underlying.Count;
    public bool IsReadOnly => false;

    public TD this[TK index] {
        get {
            TD data;
            if (_underlying.TryGetValue(index, out data)) {
                return data;
            }
            _underlying[index] = default(TD);
            return default(TD);
        }
        set {
            _underlying[index] = value;
        }
    }

    public void CopyTo(KeyValuePair<TK, TD>[] array, int arrayIndex) {
        Array.Copy(_underlying.ToArray(), 0, array, arrayIndex,
            Math.Min(array.Length - arrayIndex, _underlying.Count));
    }


    public void Add(TK key, TD value) {
        _underlying[key] = value;
    }

    public void Add(KeyValuePair<TK, TD> item) {
        _underlying[item.Key] = item.Value;
    }

    public void Clear() {
        _underlying.Clear();
    }

    public bool Contains(KeyValuePair<TK, TD> item) {
        return _underlying.Contains(item);
    }

    public bool ContainsKey(TK key) {
        return _underlying.ContainsKey(key);
    }

    public IEnumerator<KeyValuePair<TK, TD>> GetEnumerator() {
        return _underlying.GetEnumerator();
    }

    public bool Remove(TK key) {
        return _underlying.Remove(key);
    }

    public bool Remove(KeyValuePair<TK, TD> item) {
        return _underlying.Remove(item.Key);
    }

    public bool TryGetValue(TK key, out TD value) {
        return _underlying.TryGetValue(key, out value);
    }

    IEnumerator IEnumerable.GetEnumerator() {
        return _underlying.GetEnumerator();
    }
}
neural
  • 400
  • 3
  • 5
1

It could be a one-liner to check TryGetValue and return default value if it is false.

 Dictionary<string, int> myDic = new Dictionary<string, int>() { { "One", 1 }, { "Four", 4} };
 string myKey = "One"
 int value = myDic.TryGetValue(myKey, out value) ? value : 100;

myKey = "One" => value = 1

myKey = "two" => value = 100

myKey = "Four" => value = 4

Try it online

Aryan Firouzian
  • 1,940
  • 5
  • 27
  • 41
  • You have wrong "Try it online" URL. And, probably, wanted to convert "value" to int. And double used a "value" variable. But in general this answer is not bad - it is really a one-liner and exception free and allows to go deeper using the same technique and "?." operator. Good answer! – Ezh Jan 10 '23 at 12:01
1

Since .NET core 2.0 you can use:

myDict.GetValueOrDefault(someKeyKalue)
Antoine L
  • 431
  • 4
  • 6
1

One could define an interface for the key-lookup function of a dictionary. I'd probably define it as something like:

Interface IKeyLookup(Of Out TValue)
  Function Contains(Key As Object)
  Function GetValueIfExists(Key As Object) As TValue
  Function GetValueIfExists(Key As Object, ByRef Succeeded As Boolean) As TValue
End Interface

Interface IKeyLookup(Of In TKey, Out TValue)
  Inherits IKeyLookup(Of Out TValue)
  Function Contains(Key As TKey)
  Function GetValue(Key As TKey) As TValue
  Function GetValueIfExists(Key As TKey) As TValue
  Function GetValueIfExists(Key As TKey, ByRef Succeeded As Boolean) As TValue
End Interface

The version with non-generic keys would allow code that was using code using non-structure key types to allow for arbitrary key variance, which would not be possible with a generic type parameter. One should not be allowed to use a mutable Dictionary(Of Cat, String) as a mutable Dictionary(Of Animal, String) since the latter would allow SomeDictionaryOfCat.Add(FionaTheFish, "Fiona"). But there's nothing wrong with using a mutable Dictionary(Of Cat, String) as an immutable Dictionary(Of Animal, String), since SomeDictionaryOfCat.Contains(FionaTheFish) should be considered a perfectly well-formed expression (it should return false, without having to search the dictionary, for anything that isn't of type Cat).

Unfortunately, the only way one will be able to actually use such an interface is if one wraps a Dictionary object in a class which implements the interface. Depending upon what you're doing, however, such an interface and the variance it allows might make it worth the effort.

Rory O'Kane
  • 29,210
  • 11
  • 96
  • 131
supercat
  • 77,689
  • 9
  • 166
  • 211
0

What about this one-liner that checks whether a key is present using ContainsKey and then returns either the normally retreived value or the default value using the conditional operator?

var myValue = myDictionary.ContainsKey(myKey) ? myDictionary[myKey] : myDefaultValue;

No need to implement a new Dictionary class that supports default values, simply replace your lookup statements with the short line above.

Byte Commander
  • 6,506
  • 6
  • 44
  • 71
  • 5
    This has two lookups instead of one and it introduces a race condition if you're using ConcurrentDictionary. – piedar Oct 07 '16 at 14:41
0

Here is a version of @JonSkeet's for the world of C# 7.1 that also allows for an optional default to be passed in:

public static TV GetValueOrDefault<TK, TV>(this IDictionary<TK, TV> dict, TK key, TV defaultValue = default) => dict.TryGetValue(key, out TV value) ? value : defaultValue;

It may be more efficient to have two functions to optimize the case where you want to return default(TV):

public static TV GetValueOrDefault<TK, TV>(this IDictionary<TK, TV> dict, TK key, TV defaultValue) => dict.TryGetValue(key, out TV value) ? value : defaultValue;
public static TV GetValueOrDefault2<TK, TV>(this IDictionary<TK, TV> dict, TK key) {
    dict.TryGetValue(key, out TV value);
    return value;
}

Unfortunately C# doesn't (yet?) have a comma operator (or the C# 6 proposed semicolon operator) so you have to have an actual function body (gasp!) for one of the overloads.

NetMage
  • 26,163
  • 3
  • 34
  • 55
0

This question helped to confirm that the TryGetValue plays the FirstOrDefault role here.

One interesting C# 7 feature I would like to mention is the out variables feature, and if you add the null-conditional operator from C# 6 to the equation your code could be much more simple with no need of extra extension methods.

var dic = new Dictionary<string, MyClass>();
dic.TryGetValue("Test", out var item);
item?.DoSomething();

The downside of this is that you can't do everything inline like this;

dic.TryGetValue("Test", out var item)?.DoSomething();

If we'd need/want to do this we should code one extension method like Jon's.

hardkoded
  • 18,915
  • 3
  • 52
  • 64
-1

In general I would support the answer from Jon Skeet, however I prefer an implementation where I can give the default value as parameter:

public static TValue GetValueOrDefault<TKey, TValue> (this IDictionary<TKey, TValue> dictionary, TKey key, TValue defaultValue)
{
    if (dictionary.ContainsKey(key))
        return dictionary[key];
    else
        return defaultValue;
}
Damian Vogel
  • 1,050
  • 1
  • 13
  • 19
-3

No, because otherwise how would you know the difference when the key exists but stored a null value? That could be significant.

Joel Coehoorn
  • 399,467
  • 113
  • 570
  • 794
  • 11
    It could be - but in some situations you may well know that it's not. I can see this being useful sometimes. – Jon Skeet Feb 11 '09 at 20:58
  • 8
    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 – TheSoftwareJedi Feb 11 '09 at 20:59
  • 1
    By using the ContainsKey method? – MattDavey Sep 15 '11 at 15:46
  • 4
    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 Mar 24 '14 at 05:07
  • I had upvoted this. But if it was today I wouldnt have. Cant cancel now :). I upvoted the comments to make my point :) – nawfal May 27 '14 at 08:42
  • This is insignificant for this question. Besides, in many languages this is how standard types work and all is fine (ex. R) – jjaskulowski Dec 14 '16 at 09:03