C# Dictionary Design – Designing a Dictionary of Dictionaries in C#

cclassdictionary

My question: is there a canonical way of creating a dictionary of dictionaries and providing an outer/inner key pair? Is there a NugetPackage out there with an implementation?

In my code, I have now a few places where I have a property like this

private readonly IDictionary<string, IDictionary<string, SomeType>> _nestedDict = 
        new Dictionary<string, IDictionary<string, SomeType>>();

and I've created extension methods like this

public static V AddOrUpdate<T, U, V>(
    [NotNull] this IDictionary<T, IDictionary<U, V>> dictionary,
    [NotNull] T outerKey,
    [NotNull] U innerKey,
    [NotNull] Func<V> addValueFactory,
    [NotNull] Func<V, V> updateValueFactory,
    [CanBeNull] IEqualityComparer<U> innerDictionaryComparer = null)
{
    IDictionary<U, V> dict;
    if (dictionary.TryGetValue(outerKey, out dict))
    {
        V currentValue;
        if (dict.TryGetValue(innerKey, out currentValue))
        {
            var updatedValue = updateValueFactory(currentValue);
            if (!object.ReferenceEquals(updatedValue, currentValue))
                dict[innerKey] = updatedValue;
            return updatedValue;
        }
        var addedValue1 = addValueFactory();
        dict[innerKey] = addedValue1;
        return addedValue1;
    }

    var addedValue = addValueFactory();
    if (innerDictionaryComparer != null)
        dictionary[outerKey] = new Dictionary<U, V>(innerDictionaryComparer) {{innerKey, addedValue}};
    else
        dictionary[outerKey] = new Dictionary<U, V> {{innerKey, addedValue}};
    return addedValue;
}

This is powerful, but I'm starting to feel like I should just make a class for nested dictionaries. Here's my thinking:

  1. The addValueFactory and updateValueFactory are sort of clumsy and it feels like it's breaking OO principles.
  2. I am thinking about creating a ConcurrentDictionary of Dictionarys (or perhaps of ConcurrentDictionarys), and this would require some more extension methods. I sort of thought if I'm using it this much that I should just create the darn class and be done with it.

What say you?

Best Answer

There may be a simpler way towards it.

Consider this:

    public class NAryDictionary<TKey, TValue> :
        Dictionary<TKey, TValue>
    {
    }

    public class NAryDictionary<TKey1, TKey2, TValue> :
        Dictionary<TKey1, NAryDictionary<TKey2, TValue>>
    {
    }

    public class NAryDictionary<TKey1, TKey2, TKey3, TValue> :
        Dictionary<TKey1, NAryDictionary<TKey2, TKey3, TValue>>
    {
    }

Then you can write:

    class Program
    {
        static void Main(string[] args)
        {
            var dico3 = new NAryDictionary<bool, int, string, decimal>();

            dico3[false] = new NAryDictionary<int, string, decimal>();
            dico3[false][123] = new NAryDictionary<string, decimal>();
            dico3[false][123]["foo"] = 123456789.012m;

            Console.WriteLine(dico3[false][123]["foo"].ToString("0,0.000"));

            Console.ReadKey();
       }
    }

Now, although it's Visual Studio intellisense-friendly (as you'll notice with automatic code completion proposals after typing the " = " signs in the above assignments), it is arguably a bit cumbersome, still, because of those

... new NAryDictionary<bool, int, string, decimal>()
... new NAryDictionary<int, string, decimal>()

and

... new NAryDictionary<string, decimal>()

Which can be alleviated with,

    public static class NAryDictionaryExtensions
    {
        public static NAryDictionary<TKey2, TValue> New<TKey1, TKey2, TValue>(this NAryDictionary<TKey1, TKey2, TValue> dictionary)
        {
            return new NAryDictionary<TKey2, TValue>();
        }

        public static NAryDictionary<TKey2, TKey3, TValue> New<TKey1, TKey2, TKey3, TValue>(this NAryDictionary<TKey1, TKey2, TKey3, TValue> dictionary)
        {
            return new NAryDictionary<TKey2, TKey3, TValue>();
        }
    }

allowing now to write, more simply,

    class Program
    {
        static void Main(string[] args)
        {
            var dico3 = new NAryDictionary<bool, int, string, decimal>();

            dico3[true] = dico3.New();
            dico3[true][456] = dico3[true].New();
            dico3[true][456]["bar"] = 456789012.345m;

            Console.WriteLine(dico3[true][456]["bar"].ToString("0,0.000"));

            Console.ReadKey();
       }
    }

'Hope this helps.

Related Topic