DEV Community

Cover image for How a Bi-Dictionary Simplified My Unity Inventory System
Rahul Barate
Rahul Barate

Posted on

How a Bi-Dictionary Simplified My Unity Inventory System

Problem:
In my ongoing project Last Light, I’m building an inventory system. The inventory UI has different slot sections: backpack weapons, backpack consumables, equipped weapons, equipped consumables, etc.
The InventorySystem handles the data and logic, while InventoryUIController handles the UI. To make them work together, I initially used a dictionary that maps slot sections to item types. This works when displaying items from the system to the UI. But the problem shows up in reverse — when the player clicks a slot, I need to know the section type. With a normal dictionary, I’d have to traverse the entire dictionary to find the type, which isn’t efficient.

Solution:
This is where a Bi-Dictionary comes in.
A Bi-Dictionary is a data structure that supports two-way lookups: you can find a value from a key, and a key from a value — both in O(1) time. Most programming languages don’t provide this out of the box, so I wrote my own.
The idea is simple: maintain two dictionaries internally — one mapping key → value, and another mapping value → key. Then, create methods like:

  • GetValue(key) → returns the value.
  • GetKey(value) → returns the key.
  • Add(key, value) and Remove(key/value) to keep both dictionaries in sync.

Example Use Cases:

  • Inventory System (my case): Helps map item types to UI slots and back.
  • Action ↔ Sound Effect: Example: GunshotAction ↔ GunshotSound. One script can play the sound given the action, another can detect the sound and resolve it back to the action.
  • Localization: Word ↔ Translation mappings (when you need to resolve both ways).

Key takeaway:
A Bi-Dictionary is a handy data structure for cases where you need fast, two-way lookups. Many developers don’t know about it because it’s not built into most languages, but it can save time and complexity in the right scenarios.

using System;
using System.Collections.Generic;

public class BiDictionary<TKey, TValue>
{
    private Dictionary<TKey, TValue> keyToValue = new Dictionary<TKey, TValue>();
    private Dictionary<TValue, TKey> valueToKey = new Dictionary<TValue, TKey>();

    public void Add(TKey key, TValue value)
    {
        if (keyToValue.ContainsKey(key) || valueToKey.ContainsKey(value))
            throw new ArgumentException("Duplicate key or value.");

        keyToValue.Add(key, value);
        valueToKey.Add(value, key);
    }

    public TValue GetValue(TKey key)
    {
        return keyToValue[key];
    }

    public TKey GetKey(TValue value)
    {
        return valueToKey[value];
    }

    public bool TryGetValue(TKey key, out TValue value)
    {
        return keyToValue.TryGetValue(key, out value);
    }

    public bool TryGetKey(TValue value, out TKey key)
    {
        return valueToKey.TryGetValue(value, out key);
    }

    public bool RemoveByKey(TKey key)
    {
        if (!keyToValue.TryGetValue(key, out var value)) return false;
        keyToValue.Remove(key);
        valueToKey.Remove(value);
        return true;
    }

    public bool RemoveByValue(TValue value)
    {
        if (!valueToKey.TryGetValue(value, out var key)) return false;
        valueToKey.Remove(value);
        keyToValue.Remove(key);
        return true;
    }
}

Enter fullscreen mode Exit fullscreen mode

Top comments (0)