1

When building lookup tables that exceed a single dimension I find myself nesting dictionaries a lot:

public class MyLookupTable
{
    IDictionary<T1, IDictionary<T2, ISet<T3>>> mapOfMapOfSequences;

    // Update the table
    public void AddItem(T3 myItem);

    // Access the table
    public IEnumerable<T3> GetItems(T1 key1, T2 key2);
}

This has been working great for me so far, despite needing to write some wordy ContainsKey conditionals to access/modify the table.

Recently I am writing a caching mechanism that has 5 keys, and the dictionary field definition is a little ridiculous.

Architecturally, is there a design pattern I am completely overlooking that would make this problem a little more manageable?

Vesuvian
  • 697
  • 1
  • 7
  • 22
  • [Possible duplcate](https://stackoverflow.com/questions/9303859/is-a-deep-nested-dictionary-an-antipattern) – user202729 Apr 14 '18 at 13:55
  • Thank you! This is definitely pointing me in the right direction. – Vesuvian Apr 14 '18 at 14:01
  • It looks like you're doing fine. You have a custom type wrapping the dictionaries to be used properly (I'm assuming seeing the snippet you provided) and dictionaries are quite fast for lookup and storage. Personally I wouldn't suggest a better way unless I saw all the code and even then I'm sure you're doing it just fine. – Michael Puckett II Apr 14 '18 at 14:03
  • For your case a compound key would likely make sense. However it depends on the use case. If for example you need to know how many secondary key values exist for a given primary key then the nested form would make that operation more efficient. – juharr Apr 14 '18 at 14:07

1 Answers1

2

ValueTuples are helpful in creating ad-hoc types for scenarios like yours.

You could declare a dictionary like this:

IDictionary<(T1, T2, T3, T4, T5), TValue> dictionary;

Add values like this

dictionary.Add((k1, k2, k3, k4, k5), value);

You can find and example in the article Dissecting the tuples in C# 7 (MSDN).

You can also name the tuple items

IDictionary<(T1 key1, T2 key2, T3 key3, T4 key4, T5 key5), TValue> dictionary;

dictionary.Add((key1: k1, key2: k2, key3: k3, key4: k4, key5: k5), value);

Of course, you would choose speaking names adapted to your problem. Given a key, you can then access the tuple items like this:

var key = dictionary.Keys.First();
T3 k3 = key.key3;

or deconstruct the key with

var (k1, k2, k3, k4, k5) = dictionary.Keys.First();
Console.WriteLine($"Key 3 is {k3}");

C# 9 introduced record types

ValueTuples are good to be used locally; however, they are anonymous. If you want to expose a type in an API, then record classes or record structs are a better solution.

The C# compiler automatically creates the infrastructure required for dictionary keys. Among other things, it overrides Equals and GetHashCode and makes the record immutable.

All you need to write is something like this:

public readonly record struct MyKey(T1 Key1, T2 Key2, T3 Key3);

This creates a so called primary constructor with these 3 parameters as well as the whole body of the struct including 3 Key properties.

Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188