Vesuvian
Vesuvian

Reputation: 717

Better alternative to nesting dictionaries for lookup tables

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?

Upvotes: 1

Views: 598

Answers (1)

Olivier Jacot-Descombes
Olivier Jacot-Descombes

Reputation: 112259

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.

Upvotes: 2

Related Questions