0

I have a dictionary that maps strings to doubles.

I have this line:

leaves[child.Value] += branchProbability;

It crashes because instead of doing: Add default value of 0 if not found, return 0, add it to branchProbability, set new value = 0 + branchProbability, it starts by doing a get on the key which throws a KeyNotFoundException.

The only thing I can think of is:

if(!leaves.ContainsKey(child.Value))
   leaves[child.Value] = branchProbability;
else
   leaves[child.Value] += branchProbability;

But this requires an extra lookup for contains.

Would there be a more efficient way to do this while avoiding the Contains check?

jmasterx
  • 52,639
  • 96
  • 311
  • 557
  • 2
    You could use `TryGetValue` which will create the key and return the default value of the type used for your Dictionary-value (in this case, a double, which has a default value of 0.0). I my opinion, your own proposed solution is much more readable and understandable. – Lars Kristensen Apr 06 '16 at 14:01
  • Alternatively, [this answer](http://stackoverflow.com/a/2601501/717088) suggests creating extension methods for default values. There's also [another answer](http://stackoverflow.com/a/7061653/717088) to the same question, that has created an implementation of a `DefaultableDictionary` class. – Lars Kristensen Apr 06 '16 at 14:03

2 Answers2

2

The following code defines (and tests, if you paste it into LinqPad) a class that will let you do this using +=. However, if you are passing the dictionary around to lots of places, it won't work, because it has to hide the indexer with new rather than overriding it (the Dictionary's indexer isn't virtual).

One way to get around this would be by creating the DefaultingDictionary as an implementer of IDictionary<TKey, TValue> and within the class having a private member that's a Dictionary, and delegating all the calls to the methods on IDictionary through to that - except for the indexer, which you would do yourself. You still can't pass this to things expecting a Dictionary, but you could change those things to accept an IDictionary instead.

void Main()
{
    var dict = new DefaultingDictionary<string, double>();

    dict["one"] = 1;

    dict["one"] += 1;
    dict["two"] += 1;

    Console.WriteLine(dict["one"]);
    Console.WriteLine(dict["two"]);
}

class DefaultingDictionary<TKey, TValue> : Dictionary<TKey, TValue>
{
    public new TValue this[TKey key]
    {
        get
        {
            TValue v;
            if(TryGetValue(key, out v))
            {
                return v;
            }
            return default(TValue);
        }

        set
        {
            base[key] = value;
        }
    }
}

Honestly, this is all a lot of work just to be able to do the += thing. Really you should just do the check. There's no performance benefit to this, it just makes your code more complicated. If you do the check, somebody reading it will understand exactly what's going on.

Richard Irons
  • 1,433
  • 8
  • 16
-4

I am not sure if you could do much, Maybe introducing a ternary operator instead of if..else will look nice on papers. But in terms of readability, I would suggest the way you had done.

leaves[child.value] = leaves.ContainsKey(child.value) 
                     ? leaves[child.value] + branchProbability 
                     : branchProbability;
Arun Selva Kumar
  • 2,593
  • 3
  • 19
  • 30