TwoWayDictionary 1.0.0
dotnet add package TwoWayDictionary --version 1.0.0
NuGet\Install-Package TwoWayDictionary -Version 1.0.0
<PackageReference Include="TwoWayDictionary" Version="1.0.0" />
<PackageVersion Include="TwoWayDictionary" Version="1.0.0" />
<PackageReference Include="TwoWayDictionary" />
paket add TwoWayDictionary --version 1.0.0
#r "nuget: TwoWayDictionary, 1.0.0"
#:package TwoWayDictionary@1.0.0
#addin nuget:?package=TwoWayDictionary&version=1.0.0
#tool nuget:?package=TwoWayDictionary&version=1.0.0
TwoWayDictionary
A high-performance bidirectional dictionary implementation for C# that maintains one-to-one relationships between keys and values, allowing O(1) lookups in both directions.
Features
- Bidirectional Lookups: Retrieve values by keys or keys by values with O(1) time complexity
- One-to-One Mapping: Enforces unique keys and unique values - each key maps to exactly one value and vice versa
- Type-Safe: Generic implementation with compile-time type checking
- Comprehensive API: Support for Add, Set, Remove, Contains, and Try* variants
- IEnumerable Support: Iterate through key-value pairs with foreach
- Well-Tested: Comprehensive unit test coverage (40+ tests, 100% passing)
- Zero Dependencies: No external dependencies beyond .NET 6.0+
- Multi-Target: Supports .NET 6.0 and .NET 9.0
Installation
Install via NuGet Package Manager:
dotnet add package TwoWayDictionary
Or via Package Manager Console:
Install-Package TwoWayDictionary
Quick Start
using TwoWayDictionary;
// Create a two-way dictionary
var userMap = new TwoWayDictionary<int, string>();
// Add mappings
userMap.Add(101, "Alice");
userMap.Add(102, "Bob");
userMap.Add(103, "Charlie");
// Forward lookup (key -> value)
string name = userMap[101]; // "Alice"
// Reverse lookup (value -> key)
int id = userMap.GetKey("Bob"); // 102
// Check existence
bool hasUser = userMap.ContainsKey(101); // true
bool hasName = userMap.ContainsValue("Bob"); // true
// Remove mappings
userMap.RemoveByKey(102);
// or
userMap.RemoveByValue("Charlie");
API Documentation
Adding Elements
// Add - throws if key or value already exists
map.Add(key, value);
// TryAdd - returns false if key or value already exists
bool success = map.TryAdd(key, value);
// Set - replaces existing mappings if necessary
map.Set(key, value);
// Indexer set - same as Set
map[key] = value;
Retrieving Elements
// Get value by key (throws KeyNotFoundException if not found)
TValue value = map[key];
TValue value = map.GetValue(key);
// Get key by value (throws KeyNotFoundException if not found)
TKey key = map.GetKey(value);
// TryGet* variants - safe versions that don't throw
bool found = map.TryGetValue(key, out TValue value);
bool found = map.TryGetKey(value, out TKey key);
Removing Elements
// Remove by key
bool removed = map.RemoveByKey(key);
bool removed = map.Remove(key); // alias for RemoveByKey
// Remove by value
bool removed = map.RemoveByValue(value);
// Clear all
map.Clear();
Checking Existence
bool hasKey = map.ContainsKey(key);
bool hasValue = map.ContainsValue(value);
Collection Properties
int count = map.Count;
ICollection<TKey> keys = map.Keys;
ICollection<TValue> values = map.Values;
Enumeration
foreach (var pair in map)
{
Console.WriteLine($"{pair.Key} -> {pair.Value}");
}
Use Cases
User ID to Username Mapping
var userMap = new TwoWayDictionary<int, string>();
userMap.Add(1001, "john_doe");
userMap.Add(1002, "jane_smith");
// Find user by ID
string username = userMap[1001]; // "john_doe"
// Find ID by username
int userId = userMap.GetKey("jane_smith"); // 1002
Country Code to Country Name
var countryMap = new TwoWayDictionary<string, string>();
countryMap.Add("US", "United States");
countryMap.Add("UK", "United Kingdom");
countryMap.Add("CA", "Canada");
// Get full name from code
string fullName = countryMap["US"]; // "United States"
// Get code from full name
string code = countryMap.GetKey("Canada"); // "CA"
Coordinate to Entity Mapping (Game Development)
var positionMap = new TwoWayDictionary<Vector3, GameObject>();
positionMap.Add(new Vector3(0, 0, 0), player);
positionMap.Add(new Vector3(10, 0, 5), enemy);
// Find entity at position
GameObject entity = positionMap[new Vector3(0, 0, 0)];
// Find position of entity
Vector3 position = positionMap.GetKey(player);
Performance Characteristics
| Operation | Time Complexity | Space Complexity |
|---|---|---|
| Add | O(1) | O(n) |
| Remove | O(1) | O(n) |
| Get by Key | O(1) | - |
| Get by Value | O(1) | - |
| Contains | O(1) | - |
| Clear | O(n) | - |
Space Complexity: The map maintains two internal dictionaries, so memory usage is approximately 2x that of a standard Dictionary.
Thread Safety
This collection is not thread-safe. If you need to access the map from multiple threads, you must provide your own synchronization mechanism (e.g., lock statements or ReaderWriterLockSlim).
Type Constraints
Both TKey and TValue must be non-nullable reference types or value types. The notnull constraint ensures type safety and prevents null-related runtime errors.
// Valid
var map1 = new TwoWayDictionary<int, string>();
var map2 = new TwoWayDictionary<Guid, MyClass>();
// Invalid - nullable types not allowed
// var map3 = new TwoWayDictionary<int?, string>();
Requirements
- .NET 6.0 or later
- C# 12 or later
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
License
This project is licensed under the MIT License - see the LICENSE file for details.
Acknowledgments
This library was created to provide a robust, reusable bidirectional mapping solution for the C# community.
Support
If you encounter any issues or have questions, please file an issue on the GitHub repository.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net6.0
- No dependencies.
-
net9.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.0 | 100 | 1/15/2026 |
Initial release with full two-way dictionary functionality.