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; } }
Top comments (0)