How to set Dictionary as a property ? Give an example - c#

I'm trying to use a dictionary as a class member. I want to
use a property to get/set the key/value of the dictionary but I'm
confused as how to use a dictionary as a property. Since there are 2
parts, I don't know how to setup the get/sets.

You could try this:
class Example {
private Dictionary<int,string> _map;
public Dictionary<int,string> Map { get { return _map; } }
public Example() { _map = new Dictionary<int,string>(); }
}
Implementation would go along the lines of:
var e = new Example();
e.Map[42] = "The Answer";

using System;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
Console.WriteLine("Hello World");
var cl = new cl();
populate(cl.dict);
foreach(var d in cl.dict)
Console.WriteLine(d.Key);
}
private static void populate(Dictionary<int, string> d)
{
for (int i = 0; i < 10 ; i++)
{
if (!d.ContainsKey(i))
{
d.Add(i, i.ToString());
}
}
}
}
public class cl
{
public Dictionary<int, string> dict;
public cl()
{
dict = new Dictionary<int, string>();
}
}

Do you mean this ?
class MyDictionary<TKey, TValue>
{
private readonly Dictionary<TKey, TValue> _dictionary;
public void Add(TKey key, TValue value)
{
_dictionary.Add(key, value);
}
public void Clear()
{
_dictionary.Clear();
}
public bool Remve(TKey key)
{
return _dictionary.Remove(key);
}
.... and other methods...
public MyDictionary(Dictionary<TKey, TValue> dictionary)
{
_dictionary = dictionary;
}
}

Related

Dictionary of derived objects

I have a config with thousands of EnumValues and I parse them in runtime
void DoSmth()
{
foreach (var line in configLines)
{
var myEnumValue = (MyEnum) Enum.Parse(line);
...
}
}
and I can improve perfomance by creating a map between a string from the config and an actual enumValue
Dictionary<string, MyEnum> dict = new();
void DoSmth()
{
foreach (var line in configLines)
{
if (!dict.ContainsKey(line)
dict.Add(line, (MyEnum) Enum.Parse(typeof(MyEnum), line));
var myEnumValue = dict[line];
...
}
}
Q:
Is there any way (maybe using some co/contravariance magic) to create a generic function so it could create such dictionaries dynamically to avoid writing the same caching code over and over again?
e.g.
void DoSmth()
{
foreach (var line in configLines)
{
var myEnumValue = MyExtensions.Parse<MyEnum>(line);
...
}
}
class MyExtensions
{
Dictionary<Type, Dictionary<string, EnumValue> _cachedEnumValues; // < EnumValue type not exists, so how to?
public T Parse<T>(string s) where T : Enum
{
if (!_cachedEnumValues.ContansKey(typeof(T))
_cachedEnumValues.Add(typeof(T), new Dictionary<string, T>();
if (!_cachedEnumValues[typeof(T)].ContansKey(s))
_cachedEnumValues[typeof(T)].Add(s, (MyEnum) Enum.Parse(typeof(MyEnum), s);
return _cachedEnumValues[typeof(T)][s];
}
}
Sure:
public sealed class EnumHelper<T> where T : Enum
{
private static readonly ConcurrentDictionary<string, T> Cache = new ConcurrentDictionary<string, T>(StringComparer.OrdinalIgnoreCase);
public static T Parse(string s)
{
return Cache.GetOrAdd(s, k => (T)Enum.Parse(typeof(T), k));
}
}
Usage:
var t = EnumHelper<SearchOption>.Parse(SearchOption.AllDirectories.ToString());

Cannot use extension method for IDictionary<string, object>

I have some already defined extension method like this:
public static object Get(this IDictionary<string, object> dict, string key)
{
if (dict.TryGetValue(key, out object value))
{
return value;
}
return null;
}
but if I try to use it with an instance of an
IDictionary <string, myClass>
it won't show up. I thought every class derived from object. Questions:
1) Why is this happening?
2) How could I make an extension method that includes all kinds of IDictionary?
This is perfectly working:
using System.Collections.Generic;
namespace ConsoleApp1
{
public class Program
{
public static void Main(string[] args)
{
var dic = new Dictionary<string, object> {{"Test", 1}};
var result = dic.Get("Test");
}
}
public static class MyExtensions
{
public static object Get(this IDictionary<string, object> dict, string key)
{
if (dict.TryGetValue(key, out object value))
{
return value;
}
return null;
}
public static T Get<T>(this IDictionary<string, T> dict, string key)
{
if (dict.TryGetValue(key, out T value))
{
return value;
}
return default(T);
}
}
}

get keys from AppDomain.CurrentDomain.SetData()

I have implemented a very simple object cache in C# using AppDomain SetData() and GetData() like this (to reduce the number of DB calls for data that changes infrequently):
class Program
{
static void Main(string[] args)
{
List<string> users = (List<string>)GetUserList();
Console.ReadKey();
}
private static object GetUserList()
{
var users = AppDomain.CurrentDomain.GetData("GetUserList");
if (null == users)
{
users = new List<string>() { "apple", "banana" }; // dummy db results
AppDomain.CurrentDomain.SetData("GetUserList", users);
}
return users;
}
}
I now want to implement a simple PurgeCache() method iterate through the keys in my CurrentDomain and set the value to null.
How can I go about doing this?
EDIT
Based on Knaģis's reply, I have come up with the following.
ObjectCache.cs
class ObjectCache
{
private const string CacheName = "ObjectCache";
private static Dictionary<String, Object> Load()
{
Dictionary<string, object> myObjectCache = AppDomain.CurrentDomain.GetData(CacheName) as Dictionary<string, object>;
if (null == myObjectCache)
{
myObjectCache = new Dictionary<string, object>();
AppDomain.CurrentDomain.SetData(CacheName, myObjectCache);
}
return myObjectCache;
}
private static void Save(Object myObjectCache)
{
AppDomain.CurrentDomain.SetData(CacheName, myObjectCache);
}
public static void Purge()
{
Dictionary<string, object> myObjectCache = ObjectCache.Load();
myObjectCache.Clear();
ObjectCache.Save(myObjectCache);
}
public static void SetValue(String myKey, Object myValue)
{
Dictionary<string, object> myObjectCache = ObjectCache.Load();
myObjectCache[myKey] = myValue;
ObjectCache.Save(myObjectCache);
}
public static Object GetValue(String myKey)
{
Dictionary<string, object> myObjectCache = ObjectCache.Load();
return myObjectCache.ContainsKey(myKey) ? myObjectCache[myKey] : null;
}
}
Program.cs - Usage
class Program
{
static void Main(string[] args)
{
List<string> users = GetUserList<List<string>>();
ObjectCache.Purge(); // Removes Cache
Console.ReadKey();
}
private static T GetUserList<T>()
{
var users = ObjectCache.GetValue("GetUserList");
if (null == users) // No Cache
{
users = new List<string>() { "adam", "smith" }; // Dummy DB Results
ObjectCache.SetValue("GetUserList", users);
}
return (T)users;
}
}
AppDomain.GetData should not be used for caching purposes. Instead use solutions like System.Runtime.Caching. Or even just a static ConcurrentDictionary will be better.
If you insist on using AppDomain to store the values you should never delete everything in it - it stores information required by .NET framework to run properly.
Either store your values in a dictionary inside the AppDomain object or keep a list of keys yourself.
A simple in memory cache using a static dictionary (the second approach is for .NET 2.0 with explicit locks - note that this is very simple solution, there are better alternatives to locking):
using System;
using System.Collections.Concurrent;
namespace YourNamespace
{
public static class ObjectCache
{
private readonly static ConcurrentDictionary<string, object> Data = new ConcurrentDictionary<string, object>();
public static void SetValue(string key, object value)
{
Data[key] = value;
}
public static object GetValue(string key)
{
object t;
if (!Data.TryGetValue(key, out t))
return null;
return t;
}
public static void Purge()
{
Data.Clear();
}
}
public static class ObjectCache2
{
private readonly static Dictionary<string, object> Data = new Dictionary<string, object>();
public static void SetValue(string key, object value)
{
lock (Data)
Data[key] = value;
}
public static object GetValue(string key)
{
object t;
lock (Data)
{
if (!Data.TryGetValue(key, out t))
return null;
}
return t;
}
public static void Purge()
{
lock (Data)
Data.Clear();
}
}
}

Why is Lookup immutable in C#?

Unlike Dictionary, you cannot construct a Lookup by adding elements one by one. Do you happen to know the reason?
Lookup is just like multimap in C++; why can't we modify it in C#? If we really can't, how can we construct a multimap data structure in C#?
Lookup and ILookup were introduced as part of LINQ, which generally takes a more functional approach than other aspects of the framework. Personally I like the fact that Lookup is (at least publicly) immutable - and I'm looking forward to more immutable collections being available.
If you want to create your own multimap data structure, just maintain a Dictionary<TKey, List<TValue>> or something similar. You might want to look at my Edulinq implementation of Lookup for some sample code.
Here is an implementation I wrote
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
public class MultiLookup<Key, Value> : ILookup<Key, Value>
{
Dictionary<Key, HashSet<Value>> Contents = new Dictionary<Key, HashSet<Value>>();
public void Add(Key key, Value value)
{
if (!Contains(key))
{
Contents[key]=new HashSet<Value>();
}
Contents[key].Add(value);
}
public void Add(IEnumerable<Tuple<Key, Value>> items)
{
foreach (var item in items)
{
Add(item.Item1, item.Item2);
}
}
public void Remove(Key key, Value value)
{
if (!Contains(key))
{
return;
}
Contents[key].Remove(value);
if (Contents[key].Count==0)
{
Contents.Remove(key);
}
}
public void RemoveKey(Key key)
{
Contents.Remove(key);
}
public IEnumerable<Key> Keys
{
get
{
return Contents.Keys;
}
}
public int Count
{
get
{
return Contents.Count;
}
}
public bool Contains(Key key)
{
return Contents.ContainsKey(key);
}
private class Grouping : IGrouping<Key, Value>
{
public MultiLookup<Key, Value> _source;
public Key _key;
public Key Key
{
get { return _key; }
}
public static HashSet<Value> Empty = new HashSet<Value>();
public IEnumerator<Value> GetEnumerator()
{
if (!_source.Contains(_key))
{
yield break;
}
else
{
foreach (var item in _source[_key])
{
yield return item;
}
}
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
public IEnumerator<IGrouping<Key, Value>> GetEnumerator()
{
return (from p in Contents
select new Grouping() { _key = p.Key, _source = this }).GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
public IEnumerable<Value> this[Key key]
{
get { return Contents[key]; }
}
}
and a test case ( probably not exhaustive ) for you
using FluentAssertions;
using System.Linq;
using Xunit;
public class MultiLookupSpec
{
MultiLookup<int, string> Fixture = new MultiLookup<int,string>();
[Fact]
public void NewLookupShouldBeEmpty()
{
Fixture.Count.Should().Be(0);
}
[Fact]
public void AddingANewValueToANonExistentKeyShouldCreateKeyAndAddValue()
{
Fixture.Add(0, "hello");
Fixture.Count.Should().Be(1);
}
[Fact]
public void AddingMultipleValuesToAKeyShouldGenerateMultipleValues()
{
Fixture.Add(0, "hello");
Fixture.Add(0, "cat");
Fixture.Add(0, "dog");
Fixture[0].Should().BeEquivalentTo(new []{"hello", "cat", "dog"});
}
[Fact]
public void RemovingAllElementsOfKeyWillAlsoRemoveKey()
{
Fixture.Add(0, "hello");
Fixture.Add(0, "cat");
Fixture.Add(0, "dog");
Fixture.Remove(0, "dog");
Fixture.Remove(0, "cat");
Fixture.Remove(0, "hello");
Fixture.Contains(0).Should().Be(false);
}
[Fact]
public void EnumerationShouldWork()
{
Fixture.Add(0, "hello");
Fixture.Add(0, "cat");
Fixture.Add(0, "dog");
Fixture.Add(1, "house");
Fixture.Add(2, "pool");
Fixture.Add(2, "office");
Fixture.Select(s => s.Key).Should().Contain(new[] { 0, 1, 2 });
Fixture.SelectMany(s => s).Should().Contain(new[] { "hello", "cat", "dog", "house", "pool", "office" });
}
}
I had this same problem and question. Why is Lookup immutable? I solved it with some extension methods to IDictionary
public static void Add<TKey,TList,TItem>(this IDictionary<TKey,TList> dict,TKey key,TItem item)
where TList : ICollection<TItem>,new()
{
if(!dict.ContainsKey(key))
{
dict.Add(key, new TList());
}
dict[key].Add(item);
}
public static void Remove<TKey, TList, TItem>(this IDictionary<TKey, TList> dict, TKey key)
where TList : IEnumerable<TItem>, new()
{
if (dict.ContainsKey(key))
{
dict.Remove(key);
}
}
public static TList Items<TKey, TList, TItem>(this IDictionary<TKey, TList> dict, TKey key)
where TList : IEnumerable<TItem>, new()
{
if (dict.ContainsKey(key))
{
return dict[key];
}
return default(TList);
}

Two-way / bidirectional Dictionary in C#?

I want to store words in a dictionary in following way:
I can get word code by word: dict["SomeWord"] -> 123 and get word by word code: dict[123] -> "SomeWord"
Is it real? Of course one way to do it is two dictionaries: Dictionary<string,int> and Dictionary<int,string> but is there another way?
I wrote a quick couple of classes that lets you do what you want. You'd probably need to extend it with more features, but it is a good starting point.
The use of the code looks like this:
var map = new Map<int, string>();
map.Add(42, "Hello");
Console.WriteLine(map.Forward[42]);
// Outputs "Hello"
Console.WriteLine(map.Reverse["Hello"]);
//Outputs 42
Here's the definition:
public class Map<T1, T2>
{
private Dictionary<T1, T2> _forward = new Dictionary<T1, T2>();
private Dictionary<T2, T1> _reverse = new Dictionary<T2, T1>();
public Map()
{
this.Forward = new Indexer<T1, T2>(_forward);
this.Reverse = new Indexer<T2, T1>(_reverse);
}
public class Indexer<T3, T4>
{
private Dictionary<T3, T4> _dictionary;
public Indexer(Dictionary<T3, T4> dictionary)
{
_dictionary = dictionary;
}
public T4 this[T3 index]
{
get { return _dictionary[index]; }
set { _dictionary[index] = value; }
}
}
public void Add(T1 t1, T2 t2)
{
_forward.Add(t1, t2);
_reverse.Add(t2, t1);
}
public Indexer<T1, T2> Forward { get; private set; }
public Indexer<T2, T1> Reverse { get; private set; }
}
Regrettably, you need two dictionaries, one for each direction. However, you can easily get the inverse dictionary using LINQ:
Dictionary<T1, T2> dict = new Dictionary<T1, T2>();
Dictionary<T2, T1> dictInverse = dict.ToDictionary((i) => i.Value, (i) => i.Key);
Expanded on Enigmativity code by adding initializes and Contains method.
public class Map<T1, T2> : IEnumerable<KeyValuePair<T1, T2>>
{
private readonly Dictionary<T1, T2> _forward = new Dictionary<T1, T2>();
private readonly Dictionary<T2, T1> _reverse = new Dictionary<T2, T1>();
public Map()
{
Forward = new Indexer<T1, T2>(_forward);
Reverse = new Indexer<T2, T1>(_reverse);
}
public Indexer<T1, T2> Forward { get; private set; }
public Indexer<T2, T1> Reverse { get; private set; }
public void Add(T1 t1, T2 t2)
{
_forward.Add(t1, t2);
_reverse.Add(t2, t1);
}
public void Remove(T1 t1)
{
T2 revKey = Forward[t1];
_forward.Remove(t1);
_reverse.Remove(revKey);
}
public void Remove(T2 t2)
{
T1 forwardKey = Reverse[t2];
_reverse.Remove(t2);
_forward.Remove(forwardKey);
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public IEnumerator<KeyValuePair<T1, T2>> GetEnumerator()
{
return _forward.GetEnumerator();
}
public class Indexer<T3, T4>
{
private readonly Dictionary<T3, T4> _dictionary;
public Indexer(Dictionary<T3, T4> dictionary)
{
_dictionary = dictionary;
}
public T4 this[T3 index]
{
get { return _dictionary[index]; }
set { _dictionary[index] = value; }
}
public bool Contains(T3 key)
{
return _dictionary.ContainsKey(key);
}
}
}
Here is a use case, check valid parentheses
public static class ValidParenthesisExt
{
private static readonly Map<char, char>
_parenthesis = new Map<char, char>
{
{'(', ')'},
{'{', '}'},
{'[', ']'}
};
public static bool IsValidParenthesis(this string input)
{
var stack = new Stack<char>();
foreach (var c in input)
{
if (_parenthesis.Forward.Contains(c))
stack.Push(c);
else
{
if (stack.Count == 0) return false;
if (_parenthesis.Reverse[c] != stack.Pop())
return false;
}
}
return stack.Count == 0;
}
}
You could use two dictionaries, as others have said, but note also that if both TKey and TValue are the of same type (and their runtime value domains are known to be disjoint) then you can just use the same dictionary by creating two entries for each key/value pairing:
dict["SomeWord"]= "123" and dict["123"]="SomeWord"
This way a single dictionary can be used for either type of lookup.
What the heck, I'll throw my version into the mix:
public class BijectiveDictionary<TKey, TValue>
{
private EqualityComparer<TKey> _keyComparer;
private Dictionary<TKey, ISet<TValue>> _forwardLookup;
private EqualityComparer<TValue> _valueComparer;
private Dictionary<TValue, ISet<TKey>> _reverseLookup;
public BijectiveDictionary()
: this(EqualityComparer<TKey>.Default, EqualityComparer<TValue>.Default)
{
}
public BijectiveDictionary(EqualityComparer<TKey> keyComparer, EqualityComparer<TValue> valueComparer)
: this(0, EqualityComparer<TKey>.Default, EqualityComparer<TValue>.Default)
{
}
public BijectiveDictionary(int capacity, EqualityComparer<TKey> keyComparer, EqualityComparer<TValue> valueComparer)
{
_keyComparer = keyComparer;
_forwardLookup = new Dictionary<TKey, ISet<TValue>>(capacity, keyComparer);
_valueComparer = valueComparer;
_reverseLookup = new Dictionary<TValue, ISet<TKey>>(capacity, valueComparer);
}
public void Add(TKey key, TValue value)
{
AddForward(key, value);
AddReverse(key, value);
}
public void AddForward(TKey key, TValue value)
{
ISet<TValue> values;
if (!_forwardLookup.TryGetValue(key, out values))
{
values = new HashSet<TValue>(_valueComparer);
_forwardLookup.Add(key, values);
}
values.Add(value);
}
public void AddReverse(TKey key, TValue value)
{
ISet<TKey> keys;
if (!_reverseLookup.TryGetValue(value, out keys))
{
keys = new HashSet<TKey>(_keyComparer);
_reverseLookup.Add(value, keys);
}
keys.Add(key);
}
public bool TryGetReverse(TValue value, out ISet<TKey> keys)
{
return _reverseLookup.TryGetValue(value, out keys);
}
public ISet<TKey> GetReverse(TValue value)
{
ISet<TKey> keys;
TryGetReverse(value, out keys);
return keys;
}
public bool ContainsForward(TKey key)
{
return _forwardLookup.ContainsKey(key);
}
public bool TryGetForward(TKey key, out ISet<TValue> values)
{
return _forwardLookup.TryGetValue(key, out values);
}
public ISet<TValue> GetForward(TKey key)
{
ISet<TValue> values;
TryGetForward(key, out values);
return values;
}
public bool ContainsReverse(TValue value)
{
return _reverseLookup.ContainsKey(value);
}
public void Clear()
{
_forwardLookup.Clear();
_reverseLookup.Clear();
}
}
Add some data to it:
var lookup = new BijectiveDictionary<int, int>();
lookup.Add(1, 2);
lookup.Add(1, 3);
lookup.Add(1, 4);
lookup.Add(1, 5);
lookup.Add(6, 2);
lookup.Add(6, 8);
lookup.Add(6, 9);
lookup.Add(6, 10);
And then do the lookup:
lookup[2] --> 1, 6
lookup[3] --> 1
lookup[8] --> 6
You can use this extension method, although it uses enumeration, and thus may not be as performant for large data sets. If you are worried about efficiency, then you need two dictionaries. If you want to wrap the two dictionaries into one class, see the accepted answer for this question: Bidirectional 1 to 1 Dictionary in C#
public static class IDictionaryExtensions
{
public static TKey FindKeyByValue<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TValue value)
{
if (dictionary == null)
throw new ArgumentNullException("dictionary");
foreach (KeyValuePair<TKey, TValue> pair in dictionary)
if (value.Equals(pair.Value)) return pair.Key;
throw new Exception("the value is not found in the dictionary");
}
}
I made an expanded version of Enigmativity's answer available as a nuget package
https://www.nuget.org/packages/BidirectionalMap/
It is open sourced here
A modified version of Xavier John's answer, with an additional constructor to take forward and reverse Comparers. This would support case-insensitive keys, for example. Further constructors could be added, if needed, to pass further arguments to the forward and reverse Dictionary constructors.
public class Map<T1, T2> : IEnumerable<KeyValuePair<T1, T2>>
{
private readonly Dictionary<T1, T2> _forward;
private readonly Dictionary<T2, T1> _reverse;
/// <summary>
/// Constructor that uses the default comparers for the keys in each direction.
/// </summary>
public Map()
: this(null, null)
{
}
/// <summary>
/// Constructor that defines the comparers to use when comparing keys in each direction.
/// </summary>
/// <param name="t1Comparer">Comparer for the keys of type T1.</param>
/// <param name="t2Comparer">Comparer for the keys of type T2.</param>
/// <remarks>Pass null to use the default comparer.</remarks>
public Map(IEqualityComparer<T1> t1Comparer, IEqualityComparer<T2> t2Comparer)
{
_forward = new Dictionary<T1, T2>(t1Comparer);
_reverse = new Dictionary<T2, T1>(t2Comparer);
Forward = new Indexer<T1, T2>(_forward);
Reverse = new Indexer<T2, T1>(_reverse);
}
// Remainder is the same as Xavier John's answer:
// https://stackoverflow.com/a/41907561/216440
...
}
Usage example, with a case-insensitive key:
Map<int, string> categories =
new Map<int, string>(null, StringComparer.CurrentCultureIgnoreCase)
{
{ 1, "Bedroom Furniture" },
{ 2, "Dining Furniture" },
{ 3, "Outdoor Furniture" },
{ 4, "Kitchen Appliances" }
};
int categoryId = 3;
Console.WriteLine("Description for category ID {0}: '{1}'",
categoryId, categories.Forward[categoryId]);
string categoryDescription = "DINING FURNITURE";
Console.WriteLine("Category ID for description '{0}': {1}",
categoryDescription, categories.Reverse[categoryDescription]);
categoryDescription = "outdoor furniture";
Console.WriteLine("Category ID for description '{0}': {1}",
categoryDescription, categories.Reverse[categoryDescription]);
// Results:
/*
Description for category ID 3: 'Outdoor Furniture'
Category ID for description 'DINING FURNITURE': 2
Category ID for description 'outdoor furniture': 3
*/
Here's my code. Everything is O(1) except for the seeded constructors.
using System.Collections.Generic;
using System.Linq;
public class TwoWayDictionary<T1, T2>
{
Dictionary<T1, T2> _Forwards = new Dictionary<T1, T2>();
Dictionary<T2, T1> _Backwards = new Dictionary<T2, T1>();
public IReadOnlyDictionary<T1, T2> Forwards => _Forwards;
public IReadOnlyDictionary<T2, T1> Backwards => _Backwards;
public IEnumerable<T1> Set1 => Forwards.Keys;
public IEnumerable<T2> Set2 => Backwards.Keys;
public TwoWayDictionary()
{
_Forwards = new Dictionary<T1, T2>();
_Backwards = new Dictionary<T2, T1>();
}
public TwoWayDictionary(int capacity)
{
_Forwards = new Dictionary<T1, T2>(capacity);
_Backwards = new Dictionary<T2, T1>(capacity);
}
public TwoWayDictionary(Dictionary<T1, T2> initial)
{
_Forwards = initial;
_Backwards = initial.ToDictionary(kvp => kvp.Value, kvp => kvp.Key);
}
public TwoWayDictionary(Dictionary<T2, T1> initial)
{
_Backwards = initial;
_Forwards = initial.ToDictionary(kvp => kvp.Value, kvp => kvp.Key);
}
public T1 this[T2 index]
{
get => _Backwards[index];
set
{
if (_Backwards.TryGetValue(index, out var removeThis))
_Forwards.Remove(removeThis);
_Backwards[index] = value;
_Forwards[value] = index;
}
}
public T2 this[T1 index]
{
get => _Forwards[index];
set
{
if (_Forwards.TryGetValue(index, out var removeThis))
_Backwards.Remove(removeThis);
_Forwards[index] = value;
_Backwards[value] = index;
}
}
public int Count => _Forwards.Count;
public bool Contains(T1 item) => _Forwards.ContainsKey(item);
public bool Contains(T2 item) => _Backwards.ContainsKey(item);
public bool Remove(T1 item)
{
if (!this.Contains(item))
return false;
var t2 = _Forwards[item];
_Backwards.Remove(t2);
_Forwards.Remove(item);
return true;
}
public bool Remove(T2 item)
{
if (!this.Contains(item))
return false;
var t1 = _Backwards[item];
_Forwards.Remove(t1);
_Backwards.Remove(item);
return true;
}
public void Clear()
{
_Forwards.Clear();
_Backwards.Clear();
}
}
Bictionary
Here is a commingling of what I liked in each answer. It implements IEnumerable so it can use collection initializer, as you can see in the example.
Usage Constraint:
You are using different datatypes. (i.e., T1≠T2)
Code:
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
Bictionary<string, int> bictionary =
new Bictionary<string,int>() {
{ "a",1 },
{ "b",2 },
{ "c",3 }
};
// test forward lookup
Console.WriteLine(bictionary["b"]);
// test forward lookup error
//Console.WriteLine(bictionary["d"]);
// test reverse lookup
Console.WriteLine(bictionary[3]);
// test reverse lookup error (throws same error as forward lookup does)
Console.WriteLine(bictionary[4]);
}
}
public class Bictionary<T1, T2> : Dictionary<T1, T2>
{
public T1 this[T2 index]
{
get
{
if(!this.Any(x => x.Value.Equals(index)))
throw new System.Collections.Generic.KeyNotFoundException();
return this.First(x => x.Value.Equals(index)).Key;
}
}
}
Fiddle:
https://dotnetfiddle.net/mTNEuw
This is an old issue but I wanted to add a two extension methods in case anyone finds it useful. The second is not as useful but it provides a starting point if one to one dictionaries need to be supported.
public static Dictionary<VALUE,KEY> Inverse<KEY,VALUE>(this Dictionary<KEY,VALUE> dictionary)
{
if (dictionary==null || dictionary.Count == 0) { return null; }
var result = new Dictionary<VALUE, KEY>(dictionary.Count);
foreach(KeyValuePair<KEY,VALUE> entry in dictionary)
{
result.Add(entry.Value, entry.Key);
}
return result;
}
public static Dictionary<VALUE, KEY> SafeInverse<KEY, VALUE>(this Dictionary<KEY, VALUE> dictionary)
{
if (dictionary == null || dictionary.Count == 0) { return null; }
var result = new Dictionary<VALUE, KEY>(dictionary.Count);
foreach (KeyValuePair<KEY, VALUE> entry in dictionary)
{
if (result.ContainsKey(entry.Value)) { continue; }
result.Add(entry.Value, entry.Key);
}
return result;
}
Here's an alternative solution to those that were suggested. Removed the inner class and insured the coherence when adding/removing items
using System.Collections;
using System.Collections.Generic;
public class Map<E, F> : IEnumerable<KeyValuePair<E, F>>
{
private readonly Dictionary<E, F> _left = new Dictionary<E, F>();
public IReadOnlyDictionary<E, F> left => this._left;
private readonly Dictionary<F, E> _right = new Dictionary<F, E>();
public IReadOnlyDictionary<F, E> right => this._right;
public void RemoveLeft(E e)
{
if (!this.left.ContainsKey(e)) return;
this._right.Remove(this.left[e]);
this._left.Remove(e);
}
public void RemoveRight(F f)
{
if (!this.right.ContainsKey(f)) return;
this._left.Remove(this.right[f]);
this._right.Remove(f);
}
public int Count()
{
return this.left.Count;
}
public void Set(E left, F right)
{
if (this.left.ContainsKey(left))
{
this.RemoveLeft(left);
}
if (this.right.ContainsKey(right))
{
this.RemoveRight(right);
}
this._left.Add(left, right);
this._right.Add(right, left);
}
public IEnumerator<KeyValuePair<E, F>> GetEnumerator()
{
return this.left.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.left.GetEnumerator();
}
}
The following encapsulating class utilizes linq (IEnumerable Extensions) over 1 dictionary instance.
public class TwoWayDictionary<TKey, TValue>
{
readonly IDictionary<TKey, TValue> dict;
readonly Func<TKey, TValue> GetValueWhereKey;
readonly Func<TValue, TKey> GetKeyWhereValue;
readonly bool _mustValueBeUnique = true;
public TwoWayDictionary()
{
this.dict = new Dictionary<TKey, TValue>();
this.GetValueWhereKey = (strValue) => dict.Where(kvp => Object.Equals(kvp.Key, strValue)).Select(kvp => kvp.Value).FirstOrDefault();
this.GetKeyWhereValue = (intValue) => dict.Where(kvp => Object.Equals(kvp.Value, intValue)).Select(kvp => kvp.Key).FirstOrDefault();
}
public TwoWayDictionary(KeyValuePair<TKey, TValue>[] kvps)
: this()
{
this.AddRange(kvps);
}
public void AddRange(KeyValuePair<TKey, TValue>[] kvps)
{
kvps.ToList().ForEach( kvp => {
if (!_mustValueBeUnique || !this.dict.Any(item => Object.Equals(item.Value, kvp.Value)))
{
dict.Add(kvp.Key, kvp.Value);
} else {
throw new InvalidOperationException("Value must be unique");
}
});
}
public TValue this[TKey key]
{
get { return GetValueWhereKey(key); }
}
public TKey this[TValue value]
{
get { return GetKeyWhereValue(value); }
}
}
class Program
{
static void Main(string[] args)
{
var dict = new TwoWayDictionary<string, int>(new KeyValuePair<string, int>[] {
new KeyValuePair<string, int>(".jpeg",100),
new KeyValuePair<string, int>(".jpg",101),
new KeyValuePair<string, int>(".txt",102),
new KeyValuePair<string, int>(".zip",103)
});
var r1 = dict[100];
var r2 = dict[".jpg"];
}
}
This uses an indexer for the reverse lookup.
The reverse lookup is O(n) but it also does not use two dictionaries
public sealed class DictionaryDoubleKeyed : Dictionary<UInt32, string>
{ // used UInt32 as the key as it has a perfect hash
// if most of the lookup is by word then swap
public void Add(UInt32 ID, string Word)
{
if (this.ContainsValue(Word)) throw new ArgumentException();
base.Add(ID, Word);
}
public UInt32 this[string Word]
{ // this will be O(n)
get
{
return this.FirstOrDefault(x => x.Value == Word).Key;
}
}
}
There is a BijectionDictionary type available in this open source repo:
https://github.com/ColmBhandal/CsharpExtras.
It isn't qualitatively much different to the other answers given. It uses two dictionaries, like most of those answers.
What is novel, I believe, about this dictionary vs. the other answers so far, is that rather than behaving like a two way dictionary, it just behaves like a one-way, familiar dictionary and then dynamically allows you to flip the dictionary using the Reverse property. The flipped object reference is shallow, so it will still be able to modify the same core object as the original reference. So you can have two references to the same object, except one of them is flipped.
Another thing that is probably unique about this dictionary is that there are some tests written for it in the test project under that repo. It's been used by us in practice and has been pretty stable so far.

Categories