Python has a useful collection item, “defaultdict,” which initializes a default value when accessing key values that haven’t been added to the dictionary yet. It takes a user-provided function that will be called to initialize the value for new keys as they are accessed – this is useful for list values and other built in types that require initializing, and it helps streamline code that uses a dictionary type.
>>> s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)]
>>> d = defaultdict(list)
>>> for k, v in s:
... d[k].append(v)
...
>>> d.items()
[('blue', [2, 4]), ('red', [1]), ('yellow', [1, 3])]
I thought it would be useful to have this kind of behavior in C#, so decided to create a DefaultDictionary class. Here’s how it looks:
// Same behavior as python's defaultdict
public class DefaultDictionary<TKey, TValue> : Dictionary<TKey, TValue> {
Func<TValue> _init;
public DefaultDictionary(Func<TValue> init) {
_init = init;
}
public new TValue this[TKey k] {
get {
if (!ContainsKey(k))
Add(k,_init());
return base[k];
}
set => base[k] = value;
}
}
The above DefaultDictionary class takes a delegate Func that is responsible for creating an initialized value when the dictionary is used to lookup a specific key for the first time. Subsequent key accesses are passed along to the base class Dictionary.
For example:
// Default Dictionary
var dlookup = new DefaultDictionary<int, int>(() => -1);
Console.WriteLine($"Lookup 5 returns {dlookup[5]}");
// returns -1
dlookup[5] = 10;
Console.WriteLine($"Assigning to 5 OK - now returns {dlookup[5]}");
// returns 10
But dealing with Dictionaries could be a little easier. For example, avoiding having to deal with possible exceptions when attempting to add a duplicate key.
Unlike HashSet, the Dictionary class doesn’t currently have a method that lets you check, along the lines of dict.TryAdd()…, which returns a boolean advising whether the add was successful. Let’s try to make one using an extension method as follows:
// This will be in .Net 2.1 standard
static class Extensions {
public static bool TryAdd<K,V>(this Dictionary<K,V> me, K key, V value)
{
if (me.ContainsKey(key))
return false;
me.Add(key,value);
return true;
}
}
This extension method is proposed in the 2.1 standard for .NET. It allows you to do this – a little cleaner than using “ContainsKey()?” everywhere…
var lookup = new Dictionary<int,int>();
lookup.TryAdd(1,10);
lookup.TryAdd(1,5);
if (! lookup.TryAdd(1,3)) {
Console.WriteLine("We tried adding duplicate!");
}
Console.WriteLine($"[1] Still s/b 10.. {lookup[1]}");
So, now we can go forth and use Dictionaries with slightly more streamlined code.
Edit: It’s worth mentioning that ConcurrentDictionary has a “GetOrAdd()” method that does DefaultDictionary-like function of returning the result of a passed in function if the key is not found in the dictionary.