Is there a dictionary available in .NET that could hold 2 keys and one value.
Like
Dictionary(Of TKey, Of TKey, TValue)
I have a need to store two keys and at certain times look an item by the key 1 and at other times by the key 2.
My current solution is to maintain two dictionaries
Dictionary<string, long> Dict1 = new Dictionary<string, long>();
Dictionary<long, long> Dict2 = new Dictionary<long, long>();
and when need to add item I will add it to both dictionaries.
Dict1.Add("abc", 111);
Dict2.Add(345, 111);
and then I will look up an item from either one of those dictionaries depending by which one of the keys I need to look by.
Same I will do when deleting or updating an item.
I have thought about the composite key but I don't know how to set it up and I don't want to lose any speed of searching the item.
Is there some solution available in .NET to have dictionary that can hold multiple keys?
As you wish your value to be “findable” from either key, I would just use two dictionaries like you are doing now. However I would wrap this up in a class, with methods names like FindByXXX and FindByYYY.
The much harder question is how do you do a delete, as you need to know both keys at the time of the delete. Maybe your value stores both keys so you can pass the value into your delete method. Maybe you never need to remove items from the dictionaries. Or the code that needs to remove items knows both keys.
Hence there is no standard dictionary to do this, as the requirements are different between each user.
(Note you don’t want a dictionary with a composite key, as that would require you to know both keys whenever you wished to look up an item.)
Maybe, something like this:
public class TwoKeyDictionary<Tkey1, Tkey2, TValue>
{
private object m_data_lock = new object();
private Dictionary<Tkey1, Tkey2> m_dic1 = new Dictionary<Tkey1, Tkey2>();
private Dictionary<Tkey2, TValue> m_dic2 = new Dictionary<Tkey2, TValue>();
public void AddValue(Tkey1 key1, Tkey2 key2, TValue value)
{
lock(m_data_lock)
{
m_dic1[key1] = key2;
m_dic2[key2] = value;
}
}
public TValue getByKey1(Tkey1 key1)
{
lock(m_data_lock)
return m_dic2[m_dic1[key1]];
}
public TValue getByKey2(Tkey key2)
{
lock(m_data_lock)
return m_dic2[key2];
}
public void removeByKey1(Tkey1 key1)
{
lock(m_data_lock)
{
Tkey2 tmp_key2 = m_dic1[key1];
m_dic1.Remove(key1);
m_dic2.Remove(tmp_key2);
}
}
public void removeByKey2(Tkey2 key2)
{
lock(m_data_lock)
{
Tkey1 tmp_key1 = m_dic1.First((kvp) => kvp.Value.Equals(key2)).Key;
m_dic1.Remove(tmp_key1);
m_dic2.Remove(key2);
}
}
}
I can offer a second solution, but it seems more slow and ugly vs. the first.
public class TwoKeysDictionary<K1, K2, V>
{
private class TwoKeysValue<K1, K2, V>
{
public K1 Key1 { get; set; }
public K2 Key2 { get; set; }
public V Value { get; set; }
}
private List<TwoKeysValue<K1, K2, V>> m_list = new List<TwoKeysValue<K1, K2, V>>();
public void Add(K1 key1, K2 key2, V value)
{
lock (m_list)
m_list.Add(new TwoKeysValue<K1, K2, V>() { Key1 = key1, Key2 = key2, Value = value });
}
public V getByKey1(K1 key1)
{
lock (m_list)
return m_list.First((tkv) => tkv.Key1.Equals(key1)).Value;
}
public V getByKey2(K2 key2)
{
lock (m_list)
return m_list.First((tkv) => tkv.Key2.Equals(key2)).Value;
}
public void removeByKey1(K1 key1)
{
lock (m_list)
m_list.Remove(m_list.First((tkv) => tkv.Key1.Equals(key1)));
}
public void removeByKey2(K2 key2)
{
lock (m_list)
m_list.Remove(m_list.First((tkv) => tkv.Key2.Equals(key2)));
}
}
In very bad case, when Keys are a big structures (i.e. big value-types) and Keys are equals by size, and values are small value-types (for instance, a byte), with first solution you had: one set of Key1 , two sets of Key2, one set of values = 3 sets of big objects and 1 set of small values.
With second solution you had: one set of Key1 , one set of Key2, one set of values = 2 sets of big objects and small set with values.
I.e. with using of first solution you need by 50% (or by lower) more memory space vs. second, but a second solution is a very, very slow vs. first.
Your solution has a big impact on the memory footprint of your application. As the dictionary grows it will take at least double the amount memory (for value types) required to store the actual data.
You could probably approach this from a different angle. Have two dictionaries :
var lookupDictionary = new Dictionary<string, string>();
var valuesDictionary = new Dictionary<string, [YourValueType]>();
From here on in its pretty simple.
// Add a new entry into the values dictionary and give it a unique key
valuesDictionary.Add("FooBar", "FUBAR VALUE");
// Add any number of lookup keys with the same value key
lookupDictionary.Add("Foo", "FooBar");
lookupDictionary.Add("Bar", "FooBar");
lookupDictionary.Add("Rab", "FooBar");
lookupDictionary.Add("Oof", "FooBar");
When you need to find something from valuesDictionary you hit lookupDictionary first. This will give you the key of the value you are looking for in the valuesDictionary.
EDIT
I haven't addressed the deletion issue in my answer so here it goes :D
You would hit lookupDictionary to find the value key and then delete all entries from lookupDictionary that have that value.
Should be simple enough and safe since the valuesDictionary is guaranteed to have a unique key hence you will not accidentally delete a lookup key for some other value.
However, as Ian Ringrose pointed out in a comment, you are going to do a full scan on the lookupDictionary to delete. This may have an undesirable impact on performance in tight loops etc.
I can't really think of a good way to solve this issue at the moment. Perhaps someone else might have some ideas on how this could be improved.
I hope this helps.
You can't do it just with a single Dictionary without losing look up speed. The reason is that if you were to create a composite key there is no meaningful value you can return when you override GetHashCode. This means an equality comparison would need to be done against every key until a dictionary entry is found. You would also have a potential problem with a composite key in this case: because your Equals method would check whether one property or the other are equal, the following keys would essentially be duplicate keys { Id=1, Name="Bob" } { Id=1, Name="Anna" }, which doesn't give me a warm fuzzy feeling.
This leaves you with wrapping a dictionary, or pair of dictionaries with your own class.
interesting question, here's one solution.
You have to add an indexer for every key type you want to support though.
public class NewDic<T>
{
public void Add(string key1, long key2, T value)
{
mDic.Add(key1, value);
mDic.Add(key2, value);
}
public T this[string s]
{
get { return mDic[s]; }
}
public T this[long l]
{
get { return mDic[l]; }
}
Dictionary<object, T> mDic = new Dictionary<object, T>();
}
NewDic<long> dic = new NewDic<long>();
dic.Add("abc", 20, 10);
Console.WriteLine(dic["abc"]);
Console.WriteLine(dic[20]);
This is NOT a proper dictionary, but can be used for simple dictionary-like add remove functionalities.
This can be made generic as well, with proper implementation of IComparable in the keys types, and changing the dictionary code accordingly. (Note, default values of keys are not allowed to manage ambiguity!)
internal class KeyValueSet //this dictionary item is tailor made for this example
{
public string KeyStr { get; set; }
public int KeyInt { get; set; }
public int Value { get; set; }
public KeyValueSet() { }
public KeyValueSet(string keyStr, int keyInt, int value)
{
KeyStr = keyStr;
KeyInt = keyInt;
Value = value;
}
}
public class DoubleKeyDictionary
{
List<KeyValueSet> _list = new List<KeyValueSet>();
private void Add(KeyValueSet set)
{
if (set == null)
throw new InvalidOperationException("Cannot add null");
if (string.IsNullOrEmpty(set.KeyStr) && set.KeyInt == 0)
throw new InvalidOperationException("Invalid key");
if (!string.IsNullOrEmpty(set.KeyStr) && _list.Any(l => l.KeyStr.Equals(set.KeyStr))
|| set.KeyInt != 0 && _list.Any(l => l.KeyInt == set.KeyInt))
throw new InvalidOperationException("Either of keys exists");
_list.Add(set);
}
public void Add(string keyStr, int keyInt, int value)
{
Add(new KeyValueSet { KeyInt = keyInt, KeyStr = keyStr, Value = value });
}
public void Add(string key, int value)
{
Add(new KeyValueSet { KeyInt = 0, KeyStr = key, Value = value });
}
public void Add(int key, int value)
{
Add(new KeyValueSet { KeyInt = key, KeyStr = string.Empty, Value = value });
}
public void Remove(int key)
{
if (key == 0)
throw new InvalidDataException("Key not found");
var val = _list.First(l => l.KeyInt == key);
_list.Remove(val);
}
public void Remove(string key)
{
if (string.IsNullOrEmpty(key))
throw new InvalidDataException("Key not found");
var val = _list.First(l => l.KeyStr == key);
_list.Remove(val);
}
public void Remove(KeyValueSet item)
{
_list.Remove(item);
}
public int this[int index]
{
get
{
if (index != 0 && _list.Any(l => l.KeyInt == index))
return _list.First(l => l.KeyInt == index).Value;
throw new InvalidDataException("Key not found");
}
set
{
Add(index, value);
}
}
public int this[string key]
{
get
{
if (!string.IsNullOrEmpty(key) && _list.Any(l => l.KeyStr == key))
return _list.First(l => l.KeyStr == key).Value;
throw new InvalidDataException("Key not found");
}
set
{
Add(key, value);
}
}
}
Testing the DoubleKeyDictionary
var dict = new DoubleKeyDictionary();
dict.Add(123, 1);
dict.Add(234, 2);
dict.Add("k1", 3);
dict.Add("k2", 4);
dict[456] = 5;
dict["k3"] = 6;
dict.Add("k4", 567, 7);
dict.Remove(123);
Console.WriteLine(dict[234]); //2
Console.WriteLine(dict["k2"]); //4
Console.WriteLine(dict[456]); //5
Console.WriteLine(dict[567]); //7
Console.WriteLine(dict["k4"]); //7
Console.WriteLine(dict[123]); //exception
As a local solution I use the easy approach:
Imagine I have a collection of products identified by a string and a form with buttons for each one of the products.
When managing the state of the buttons I need to find buttons by string key.
When handling the clicks I need to find product IDs by button instance.
Instead of maintaining two separate dictionaries I do the following:
public class SPurchaseOption
{
public Button Button;
public string ProductID;
public string SomeOtherAssociatedData;
}
Dictionary<object, SPurchaseOption> purchaseOptions;
When the buttons are initialized I append two entries into the Dictionary i.e.
Key: ProductID, Value: "SPurchaseOption"
Key: Button, Value: "SPurchaseOption"
For a more general approach and if you need a commonly used component you will have to build a wrap around two dictionaries i.e:
public class DoubleKeyedDictionary<TKey1, TKey2, TValue>
{
class SItem
{
public TKey1 key1;
public TKey2 key2;
public TValue value;
}
Dictionary<TKey1, SItem> dic1;
Dictionary<TKey2, SItem> dic2;
}
this will give access to both the value and alternative key by any of the keys.
As suggested in a comment to your question you could simply use an Object key for your Dictionary:
Dictionary<Object, long> dict = new Dictionary<Object, long>();
dict.Add("abc", 111);
dict.Add(345, 111);
To get a cleaner solution you could wrap this dictionary in a custom class and create your version of Add method:
public void Add(ISet<Object> keys, T value){
foreach(Object k in keys)
{
_privateDict.Add(k, value);
}
}
How about a Dictionary<Tuple<string, long>, long>? Tuples are compared by value, so it should index uniquely in the expected manner. Plus, now you won't have to pack the long value in two places (and deal with the wonderful pain of synchronizing the values everywhere).
How about this approach? Basically, still use a dictionary-based strategy, but facade it through a class with overloaded indexer properties. So it looks like a dictionary, feels like a dictionary, but supports multiple keys (not like a dictionary, LOL).
public class MultiKeyDictionary<TFirstKey, TSecondKey, TValue>
{
private readonly Dictionary<TFirstKey, TValue> firstKeyDictionary =
new Dictionary<TFirstKey, TValue>();
private readonly Dictionary<TSecondKey, TFirstKey> secondKeyDictionary =
new Dictionary<TSecondKey, TFirstKey>();
public TValue this[TFirstKey idx]
{
get
{
return firstKeyDictionary[idx];
}
set
{
firstKeyDictionary[idx] = value;
}
}
public TValue this[TSecondKey idx]
{
get
{
var firstKey = secondKeyDictionary[idx];
return firstKeyDictionary[firstKey];
}
set
{
var firstKey = secondKeyDictionary[idx];
firstKeyDictionary[firstKey] = value;
}
}
public IEnumerable<KeyValuePair<TFirstKey, TValue>> GetKeyValuePairsOfFirstKey()
{
return firstKeyDictionary.ToList();
}
public IEnumerable<KeyValuePair<TSecondKey, TValue>> GetKeyValuePairsOfSecondKey()
{
var r = from s in secondKeyDictionary
join f in firstKeyDictionary on s.Value equals f.Key
select new KeyValuePair<TSecondKey, TValue>(s.Key, f.Value);
return r.ToList();
}
public void Add(TFirstKey firstKey, TSecondKey secondKey, TValue value)
{
firstKeyDictionary.Add(firstKey, value);
secondKeyDictionary.Add(secondKey, firstKey);
}
public bool Remove(TFirstKey firstKey)
{
if (!secondKeyDictionary.Any(f => f.Value.Equals(firstKey))) return false;
var secondKeyToDelete = secondKeyDictionary.First(f => f.Value.Equals(firstKey));
secondKeyDictionary.Remove(secondKeyToDelete.Key);
firstKeyDictionary.Remove(firstKey);
return true;
}
public bool Remove(TSecondKey secondKey)
{
if (!secondKeyDictionary.ContainsKey(secondKey)) return false;
var firstKey = secondKeyDictionary[secondKey];
secondKeyDictionary.Remove(secondKey);
firstKeyDictionary.Remove(firstKey);
return true;
}
}
Test the code...
static void Main(string[] args)
{
var dict = new MultiKeyDictionary<string, long, long>();
dict.Add("abc", 111, 1234);
dict.Add("def", 222, 7890);
dict.Add("hij", 333, 9090);
Console.WriteLine(dict["abc"]); // expect 1234
Console.WriteLine(dict["def"]); // expect 7890
Console.WriteLine(dict[333]); // expect 9090
Console.WriteLine();
Console.WriteLine("removing def");
dict.Remove("def");
Console.WriteLine();
Console.WriteLine("now we have:");
foreach (var d in dict.GetKeyValuePairsOfFirstKey())
{
Console.WriteLine($"{d.Key} : {d.Value}");
}
Console.WriteLine();
Console.WriteLine("removing 333");
dict.Remove(333);
Console.WriteLine();
Console.WriteLine("now we have:");
foreach (var d in dict.GetKeyValuePairsOfSecondKey())
{
Console.WriteLine($"{d.Key} : {d.Value}");
}
Console.ReadLine();
}
At first I thought I could create a class that implmented IDictionary<TKey1, TValue> and IDictionary<TKey2, TValue>, and just have a single Dictionary as a field and delegate most methods to the single dictionary with minimal logic.
The problem with this approach is that TKey1 and TKey2 could be of the same type, which is a problem because this new class would be implementing the same interface twice. Which method should the runtime invoke when TKey1 is a string and TKey2 is also a string?
As others above have suggested, it is best to create your own data structure that utilizes one or two dictionaries behind the scenes. For example, if you knew ahead of time that you wanted to use a string and an int as your keys, you could use this approach:
public class StringIntDictionary<TValue> : IDictionary<string, TValue>, IDictionary<int, TValue>
{
private IDictionary<object, TValue> _dictionary = new Dictionary<object, TValue>();
// implement interface below, delegate to _dictionary
}
That would allow you to look use both string and int keys:
var dict = StringIntDictionary<bool>();
dict["abc"] = true;
dict[123] = true;
A very crude way that may suffice until I find a better one.
class MyClass
{
public string StringKey = "";
public int IntKey = 0;
public override Equals(object obj)
{
// Code to return true if all fields are equal
}
}
Dictionary <MyClass, string> MyDict;
MyClass myClass;
MyDict[MyDict.Keys.FirstOrDefault(x => x.Equals(MyClass))];
For my money, the answer saying to use tuples is the right one. Unfortunately, my NuGet is too old to get the ValueTuple package I'd want to use so my fields aren't 'item1', 'item2' etc. That would be more confusing than what I've done here. When I change VS/NuGet versions, it's ValueTuples all the way for this kind of situation. Second time this week I've encountered the need!
Here's something better in terms of efficiency.
public class MultiKeyDictionary<TKeyType1, TKeyType2, TValueType>
{
private readonly object threadLock = new object();
private readonly Dictionary<TKeyType1, TValueType> _dictionary1 = new Dictionary<TKeyType1, TValueType>();
private readonly Dictionary<TKeyType2, TValueType> _dictionary2 = new Dictionary<TKeyType2, TValueType>();
private readonly Dictionary<TKeyType1, TKeyType2> _Key1Key2Map = new Dictionary<TKeyType1, TKeyType2>();
private readonly Dictionary<TKeyType2, TKeyType1> _Key2Key1Map = new Dictionary<TKeyType2, TKeyType1>();
public bool Add(TKeyType1 key1, TKeyType2 key2, TValueType v)
{
if (ContainsKey1(key1) || ContainsKey2(key2))
return false;
_dictionary1.Add(key1, v);
_dictionary2.Add(key2, v);
_Key1Key2Map.Add(key1, key2);
_Key2Key1Map.Add(key2, key1);
return true;
}
public bool ContainsKey1(TKeyType1 key)
{
return _dictionary1.ContainsKey(key);
}
public bool ContainsKey2(TKeyType2 key)
{
return _dictionary2.ContainsKey(key);
}
//Note if TKeyType1 and TKeyType2 are the same then we are forced to use GetBy functions
public TValueType GetByKey1(TKeyType1 key)
{
return _dictionary1[key];
}
public TValueType GetByKey2(TKeyType2 key)
{
return _dictionary2[key];
}
public bool SetByKey1(TKeyType1 key, TValueType val)
{
if (ContainsKey1(key))
return false;
lock (threadLock)
{
var key2 = _Key1Key2Map[key];
_dictionary1[key] = val;
_dictionary2[key2] = val;
}
return true;
}
public bool SetByKey2(TKeyType2 key, TValueType val)
{
if (ContainsKey2(key))
return false;
lock (threadLock)
{
var key1 = _Key2Key1Map[key];
_dictionary1[key1] = val;
_dictionary2[key] = val;
}
return true;
}
public void RemoveUsingKey1(TKeyType1 key)
{
lock (threadLock)
{
var key2 = _Key1Key2Map[key];
_dictionary1.Remove(key);
_dictionary2.Remove(key2);
_Key1Key2Map.Remove(key);
_Key2Key1Map.Remove(key2);
}
}
public void RemoveUsingKey2(TKeyType2 key)
{
lock (threadLock)
{
var key1 = _Key2Key1Map[key];
_dictionary1.Remove(key1);
_dictionary2.Remove(key);
_Key1Key2Map.Remove(key1);
_Key2Key1Map.Remove(key);
}
}
public bool Contains(TKeyType1 key)
{
return _dictionary1.ContainsKey(key);
}
public bool Contains(TKeyType2 key)
{
return _dictionary2.ContainsKey(key);
}
public TValueType this[TKeyType1 key]
{
get => GetByKey1(key);
set => SetByKey1(key, value);
}
public TValueType this[TKeyType2 key]
{
get => GetByKey2(key);
set => SetByKey2(key, value);
}
public void Remove(TKeyType1 key)
{
RemoveUsingKey1(key);
}
public void Remove(TKeyType2 key)
{
RemoveUsingKey2(key);
}
public int Count => _dictionary1.Count;
public Dictionary<TKeyType1, TValueType>.KeyCollection Key1s => _dictionary1.Keys;
public Dictionary<TKeyType2, TValueType>.KeyCollection Key2s => _dictionary2.Keys;
public Dictionary<TKeyType1, TValueType>.ValueCollection Values => _dictionary1.Values;
public void Clear()
{
lock (threadLock)
{
_dictionary1.Clear();
_dictionary2.Clear();
_Key1Key2Map.Clear();
_Key2Key1Map.Clear();
}
}
//Map between Keys
public TKeyType2 Key2(TKeyType1 key)
{
return _Key1Key2Map[key];
}
public TKeyType1 Key1(TKeyType2 key)
{
return _Key2Key1Map[key];
}
}
I have created my own version of it. It is a bit more sophisticated. The documentation should explain some of the functionality. In essence it allows a reasonable way to handle 2 keys for the same value, auto-merges entries, is presumably thread-safe (untested), allows mapping keys together, and handles deleting entries, all the while having the functionality of dictionaries at its base. When adding an entry, but one of its keys already exists, it will just add the key and overwrite the value. It's quite distinct logically from other forms of collections, so these are the most I was able to implement.
Some structure with key pairs didn't seem fitting, given that I need this to arbitrarily add a second key as needed to existing entries, and given the merging functionality. I also took regard for the situation where one uses the same types for both keys, but also for situations where they don't.
/// <summary> A collection that internally uses a list (which in turn internally uses an array), and two dictionaries for the index.
/// This allows operating it based on two keys and provides means to (automatically) map keys to each other.
/// The indexing of the internal list is treated specially. In order to not infringe on the validity of the dictionaries' references to the indexes,
/// they are kept identical. Removing is handled by setting the entries to 'null', and once a new item is added, they are overwritten. </summary>
/// <typeparam name="TKey1"> The first key. </typeparam>
/// <typeparam name="TKey2"> The second key. </typeparam>
/// <typeparam name="T"> The stored value type. </typeparam>
public class TwoKeyDictionary<TKey1, TKey2, T> : IEnumerable<TwoKeyDictionaryEntry<TKey1, TKey2, T>>, IReadOnlyCollection<TwoKeyDictionaryEntry<TKey1, TKey2, T>>
{
private readonly Dictionary<TKey1, int> _keys01 = new Dictionary<TKey1, int> ();
private readonly Dictionary<TKey2, int> _keys02 = new Dictionary<TKey2, int> ();
private readonly List<TwoKeyDictionaryEntry<TKey1, TKey2, T>> _items = new List<TwoKeyDictionaryEntry<TKey1, TKey2, T>> ();
private int _freeIndex = 0; // The index of the first free slot.
private int _freeCount = 0; // Free before the last value.
private readonly object _lock = new object ();
public TwoKeyDictionary () { }
/// <summary> Adds an item. </summary>
public bool Add (TKey1 key, T value)
{
return AddByKey1 (key, value);
}
/// <summary> Adds an item. </summary>
public bool Add (TKey2 key, T value)
{
return AddByKey2 (key, value);
}
/// <summary> Adds an item. </summary>
public bool AddByKey1 (TKey1 key, T value)
{
lock (_lock)
{
return AddByKey1Internal (key, value);
}
}
/// <summary> Adds an item. </summary>
public bool AddByKey2 (TKey2 key, T value)
{
lock (_lock)
{
return AddByKey2Internal (key, value);
}
}
/// <summary> Adds an item with two keys. If either key already exists, it will map the other key to it. The value will only be overwritten if it's 'null'. </summary>
public bool Add (TKey1 key1, TKey2 key2, T value)
{
return Add (key1, key2, value, false);
}
/// <summary> Adds an item with two keys. If either key already exists, it will map the other key to it. The value will only be overwritten if it's 'null'.
/// This may also define how the key is mapped, if occurring. </summary>
public bool Add (TKey1 key1, TKey2 key2, T value, bool mapToKey2)
{
lock (_lock)
{
return AddInternal (key1, key2, value, mapToKey2);
}
}
/// <summary> Maps both keys together. If either key exists, it will add the other one to it. If both exist, it will merge the entries and delete the other.
/// By default this will map to key1. </summary>
public bool Map (TKey1 key1, TKey2 key2)
{
return MapToKey1 (key1, key2);
}
/// <summary> Maps both keys together. If either key exists, it will add the other one to it. If both exist, it will merge the entries and delete the one with key2. </summary>
public bool MapToKey1 (TKey1 key1, TKey2 key2)
{
lock (_lock)
{
return MapToKey1Internal (key1, key2);
}
}
/// <summary> Maps both keys together. If either key exists, it will add the other one to it. If both exist, it will merge the entries and delete the one with key1. </summary>
public bool MapToKey2 (TKey1 key1, TKey2 key2)
{
lock (_lock)
{
return MapToKey2Internal (key1, key2);
}
}
/// <summary> Removes an entry based on key1. If there is a key2 mapped to it, it will be removed as well. </summary>
public bool Remove (TKey1 key)
{
return RemoveByKey1 (key);
}
/// <summary> Removes an entry based on key2. If there is a key1 mapped to it, it will be removed as well. </summary>
public bool Remove (TKey2 key)
{
return RemoveByKey2 (key);
}
/// <summary> Removes an entry based on key1. If there is a key2 mapped to it, it will be removed as well. </summary>
public bool RemoveByKey1 (TKey1 key)
{
lock (_lock)
{
return RemoveByKey1Internal (key);
}
}
/// <summary> Removes an entry based on key2. If there is a key1 mapped to it, it will be removed as well. </summary>
public bool RemoveByKey2 (TKey2 key)
{
lock (_lock)
{
return RemoveByKey2Internal (key);
}
}
/// <summary> Removes an entry based on both, key1 and key2. Any entries related to either keys will be removed. </summary>
public bool Remove (TKey1 key1, TKey2 key2)
{
lock (_lock)
{
return RemoveByKey1Internal (key1) | RemoveByKey2Internal (key2);
}
}
/// <summary> Tries to return a value based on key1. </summary>
public bool TryGetValue (TKey1 key, out T value)
{
return TryGetValueByKey1 (key, out value);
}
/// <summary> Tries to return a value based on key2. </summary>
public bool TryGetValue (TKey2 key, out T value)
{
return TryGetValueByKey2 (key, out value);
}
/// <summary> Tries to return a value based on key1. </summary>
public bool TryGetValueByKey1 (TKey1 key, out T value)
{
if (key == null) { value = default; return false; }
if (_keys01.TryGetValue (key, out int index))
{
TwoKeyDictionaryEntry<TKey1, TKey2, T> entry = _items[index];
if (entry != null)
{
value = entry.Value;
return true;
}
}
value = default;
return false;
}
/// <summary> Tries to return a value based on key2. </summary>
public bool TryGetValueByKey2 (TKey2 key, out T value)
{
if (key == null) { value = default; return false; }
if (_keys02.TryGetValue (key, out int index))
{
TwoKeyDictionaryEntry<TKey1, TKey2, T> entry = _items[index];
if (entry != null)
{
value = entry.Value;
return true;
}
}
value = default;
return false;
}
/// <summary> Tries to return a value based on key1 or key2. Prioritizes key1. </summary>
public bool TryGetValue (TKey1 key1, TKey2 key2, out T value)
{
return TryGetValue (key1, key2, false, out value);
}
/// <summary> Tries to return a value based on key1 or key2. </summary>
public bool TryGetValue (TKey1 key1, TKey2 key2, bool prioritizeKey2, out T value)
{
return prioritizeKey2 ? TryGetValue (key1, out value) || TryGetValue (key2, out value) : TryGetValue (key2, out value) || TryGetValue (key1, out value);
}
/// <summary> Returns 'true' if they key and the entry still exists. The stored value itself may still be 'null' regardless. </summary>
public bool ContainsKey (TKey1 key)
{
return ContainsKey1 (key);
}
/// <summary> Returns 'true' if they key and the entry still exists. The stored value itself may still be 'null' regardless. </summary>
public bool ContainsKey (TKey2 key)
{
return ContainsKey2 (key);
}
/// <summary> Returns 'true' if they key and the entry still exists. The stored value itself may still be 'null' regardless. </summary>
public bool ContainsKey1 (TKey1 key)
{
if (key == null) return false;
if (_keys01.TryGetValue (key, out int index)) return _items[index] != null;
else return false;
}
/// <summary> Returns 'true' if they key and the entry still exists. The stored value itself may still be 'null' regardless. </summary>
public bool ContainsKey2 (TKey2 key)
{
if (key == null) return false;
if (_keys02.TryGetValue (key, out int index)) return _items[index] != null;
else return false;
}
/// <summary> Returns 'true' if they key and the entry still exists. The stored value itself may still be 'null' regardless. </summary>
public bool ContainsKey (TKey1 key1, TKey2 key2)
{
return ContainsKey1 (key1) || ContainsKey2 (key2);
}
#region Internal
// Returns true if this wasn't the last position.
private bool GetFreeIndex (bool apply, out int index)
{
if (_freeCount == 0)
{
index = _items.Count;
return false;
}
else
{
index = _freeIndex;
if (apply)
{
// We must find the next free slot.
int freeIndex = _freeIndex + 1;
int count = _items.Count;
while (freeIndex < count && _items[freeIndex] != null)
{
freeIndex++;
}
if (freeIndex == count) _freeCount = 0;
else Interlocked.Decrement (ref _freeCount);
_freeIndex = freeIndex;
}
return true;
}
}
private bool MapToKey1Internal (TKey1 key1, TKey2 key2)
{
if (key1 == null || key2 == null) return false;
bool s1 = _keys01.TryGetValue (key1, out int index1);
bool s2 = _keys02.TryGetValue (key2, out int index2);
if (s1 && s2)
{
TwoKeyDictionaryEntry<TKey1, TKey2, T> e1 = _items[index1];
TwoKeyDictionaryEntry<TKey1, TKey2, T> e2 = _items[index2];
RemoveByKey2Internal (key2);
e1.Key2 = key2;
if (e1.Value == null) e1.Value = e2.Value;
return true;
}
else if (s1)
{
_items[index1].Key2 = key2;
_keys02.Add (key2, index1);
return true;
}
else if (s2)
{
_items[index2].Key1 = key1;
_keys01.Add (key1, index2);
return true;
}
else return false;
}
private bool MapToKey2Internal (TKey1 key1, TKey2 key2)
{
if (key1 == null || key2 == null) return false;
bool s1 = _keys01.TryGetValue (key1, out int index1);
bool s2 = _keys02.TryGetValue (key2, out int index2);
if (s1 && s2)
{
TwoKeyDictionaryEntry<TKey1, TKey2, T> e1 = _items[index1];
TwoKeyDictionaryEntry<TKey1, TKey2, T> e2 = _items[index2];
RemoveByKey1Internal (key1);
e2.Key1 = key1;
if (e2.Value == null) e2.Value = e1.Value;
return true;
}
else if (s1)
{
_items[index1].Key2 = key2;
return true;
}
else if (s2)
{
_items[index2].Key1 = key1;
return true;
}
else return false;
}
private bool AddByKey1Internal (TKey1 key, T value)
{
if (key == null) return false;
if (_keys01.TryGetValue (key, out int index))
{
TwoKeyDictionaryEntry<TKey1, TKey2, T> entry = _items[index];
if (entry != null)
{
entry.Value = value;
return true;
}
else
{
_keys01.Remove (key);
return AddByKey1Internal (key, value);
}
}
else
{
TwoKeyDictionaryEntry<TKey1, TKey2, T> item = new TwoKeyDictionaryEntry<TKey1, TKey2, T> (key, default, value);
if (GetFreeIndex (true, out int freeIndex))
{
_items[freeIndex] = item;
}
else
{
_items.Add (item);
}
_keys01.Add (key, freeIndex);
return true;
}
}
private bool AddByKey2Internal (TKey2 key, T value)
{
if (key == null) return false;
if (_keys02.TryGetValue (key, out int index))
{
TwoKeyDictionaryEntry<TKey1, TKey2, T> entry = _items[index];
if (entry != null)
{
entry.Value = value;
return true;
}
else
{
_keys02.Remove (key);
return AddByKey2Internal (key, value);
}
}
else
{
TwoKeyDictionaryEntry<TKey1, TKey2, T> item = new TwoKeyDictionaryEntry<TKey1, TKey2, T> (default, key, value);
if (GetFreeIndex (true, out int freeIndex))
{
_items[freeIndex] = item;
}
else
{
_items.Add (item);
}
_keys02.Add (key, freeIndex);
return true;
}
}
private bool AddInternal (TKey1 key1, TKey2 key2, T value, bool mapToKey2)
{
if (key1 == null) return AddByKey2Internal (key2, value);
else if (key2 == null) return AddByKey1Internal (key1, value);
bool hasKey1 = _keys01.TryGetValue (key1, out int index1);
bool hasKey2 = _keys02.TryGetValue (key2, out int index2);
if (hasKey1 && hasKey2)
{
// We have 2 different entries' keys that point to the same value. Merge them to one key, remove the other.
if (mapToKey2)
{
if (MapToKey2Internal (key1, key2))
{
_items[index2].Value = value;
}
}
else
{
if (MapToKey1Internal (key1, key2))
{
_items[index1].Value = value;
}
}
}
else if (hasKey1)
{
TwoKeyDictionaryEntry<TKey1, TKey2, T> entry = _items[index1];
entry.Key2 = key2;
entry.Value = value;
}
else if (hasKey2)
{
TwoKeyDictionaryEntry<TKey1, TKey2, T> entry = _items[index2];
entry.Key1 = key1;
entry.Value = value;
}
else
{
_items.Add (new TwoKeyDictionaryEntry<TKey1, TKey2, T> (key1, key2, value));
}
return true;
}
private bool RemoveByKey1Internal (TKey1 key)
{
if (key == null) return false;
if (_keys01.TryGetValue (key, out int index))
{
TwoKeyDictionaryEntry<TKey1, TKey2, T> entry = _items[index];
if (entry != null)
{
_keys01.Remove (key);
if (entry.Key2 != null) _keys02.Remove (entry.Key2);
if (index == _items.Count - 1)
{
_items.RemoveAt (index);
}
else
{
_items[index] = null;
_freeIndex = _freeCount > 0 ? Math.Min (_freeIndex, index) : index;
Interlocked.Increment (ref _freeCount);
}
return true;
}
else
{
_keys01.Remove (key);
}
}
return false;
}
private bool RemoveByKey2Internal (TKey2 key)
{
if (key == null) return false;
if (_keys02.TryGetValue (key, out int index))
{
TwoKeyDictionaryEntry<TKey1, TKey2, T> entry = _items[index];
if (entry != null)
{
_keys02.Remove (key);
if (entry.Key1 != null) _keys01.Remove (entry.Key1);
if (index == _items.Count - 1)
{
_items.RemoveAt (index);
}
else
{
_items[index] = null;
_freeIndex = _freeCount > 0 ? Math.Min (_freeIndex, index) : index;
Interlocked.Increment (ref _freeCount);
}
return true;
}
else
{
_keys02.Remove (key);
}
}
return false;
}
#endregion
#region Interface Implementations
public int Count => _items.Count (j => j != null);
public IEnumerator<TwoKeyDictionaryEntry<TKey1, TKey2, T>> GetEnumerator ()
{
return _items.Where (j => j != null).GetEnumerator ();
}
IEnumerator IEnumerable.GetEnumerator ()
{
return _items.Where (j => j != null).GetEnumerator ();
}
#endregion
}
/// <summary> The entry class of <see cref="TwoKeyDictionary{TKey1, TKey2, T}"/>, which grants references to the keys in both dictionaries used. </summary>
/// <typeparam name="TKey1"> The first key. </typeparam>
/// <typeparam name="TKey2"> The second key. </typeparam>
/// <typeparam name="T"> The stored value type. </typeparam>
public class TwoKeyDictionaryEntry<TKey1, TKey2, T>
{
public TKey1 Key1 { get; internal set; }
public TKey2 Key2 { get; internal set; }
public T Value { get; internal set; }
internal TwoKeyDictionaryEntry () { }
internal TwoKeyDictionaryEntry (TKey1 key1, TKey2 key2, T value)
{
Key1 = key1;
Key2 = key2;
Value = value;
}
public override string ToString ()
{
return $"{Key1?.ToString () ?? "---"} | {Key2?.ToString () ?? "---"} | {Value}";
}
}
I tried:
NameValueCollection Data = new NameValueCollection();
Data.Add("foo","baa");
string json = new JavaScriptSerializer().Serialize(Data);
it returns: ["foo"] I expected {"foo" : "baa"}
How do I to do this?
One way to serialize NameValueCollection is by first converting it to Dictionary and then serialize the Dictionary. To convert to dictionary:
thenvc.AllKeys.ToDictionary(k => k, k => thenvc[k]);
If you need to do the conversion frequently, you can also create an extension method to NameValueCollection:
public static class NVCExtender
{
public static IDictionary<string, string> ToDictionary(
this NameValueCollection source)
{
return source.AllKeys.ToDictionary(k => k, k => source[k]);
}
}
so you can do the conversion in one line like this:
NameValueCollection Data = new NameValueCollection();
Data.Add("Foo", "baa");
var dict = Data.ToDictionary();
Then you can serialize the dictionary:
var json = new JavaScriptSerializer().Serialize(dict);
// you get {"Foo":"baa"}
But NameValueCollection can have multiple values for one key, for example:
NameValueCollection Data = new NameValueCollection();
Data.Add("Foo", "baa");
Data.Add("Foo", "again?");
If you serialize this you will get {"Foo":"baa,again?"}.
You can modify the converter to produce IDictionary<string, string[]> instead:
public static IDictionary<string, string[]> ToDictionary(
this NameValueCollection source)
{
return source.AllKeys.ToDictionary(k => k, k => source.GetValues(k));
}
So you can get serialized value like this: {"Foo":["baa","again?"]}.
NameValueCollection isn't an IDictionary, so the JavaScriptSerializer cannot serialize it as you expect directly. You'll need to first convert it into a dictionary, then serialize it.
Update: following questions regarding multiple values per key, the call to nvc[key] will simply return them separated by a comma, which may be ok. If not, one can always call GetValues and decide what to do with the values appropriately. Updated the code below to show one possible way.
public class StackOverflow_7003740
{
static Dictionary<string, object> NvcToDictionary(NameValueCollection nvc, bool handleMultipleValuesPerKey)
{
var result = new Dictionary<string, object>();
foreach (string key in nvc.Keys)
{
if (handleMultipleValuesPerKey)
{
string[] values = nvc.GetValues(key);
if (values.Length == 1)
{
result.Add(key, values[0]);
}
else
{
result.Add(key, values);
}
}
else
{
result.Add(key, nvc[key]);
}
}
return result;
}
public static void Test()
{
NameValueCollection nvc = new NameValueCollection();
nvc.Add("foo", "bar");
nvc.Add("multiple", "first");
nvc.Add("multiple", "second");
foreach (var handleMultipleValuesPerKey in new bool[] { false, true })
{
if (handleMultipleValuesPerKey)
{
Console.WriteLine("Using special handling for multiple values per key");
}
var dict = NvcToDictionary(nvc, handleMultipleValuesPerKey);
string json = new JavaScriptSerializer().Serialize(dict);
Console.WriteLine(json);
Console.WriteLine();
}
}
}
If your dictionary is not intended to contain many entries, you can use the class:
System.Collections.Specialized.ListDictionary
For completeness' sake, and because the question continues to get asked (e.g. here), as long as you are using Json.NET or DataContractJsonSerializer (but not JavaScriptSerializer), you could use the adapter pattern and wrap the NameValueCollection in an IDictionary<string, string[]> adapter, and serialize that using any serializer that fully supports serializing arbitrary dictionaries.
Once such adapter is as follows:
public class NameValueCollectionDictionaryAdapter<TNameValueCollection> : IDictionary<string, string[]>
where TNameValueCollection : NameValueCollection, new()
{
readonly TNameValueCollection collection;
public NameValueCollectionDictionaryAdapter() : this(new TNameValueCollection()) { }
public NameValueCollectionDictionaryAdapter(TNameValueCollection collection)
{
this.collection = collection;
}
// Method instead of a property to guarantee that nobody tries to serialize it.
public TNameValueCollection GetCollection() { return collection; }
#region IDictionary<string,string[]> Members
public void Add(string key, string[] value)
{
if (collection.GetValues(key) != null)
throw new ArgumentException("Duplicate key " + key);
if (value == null)
collection.Add(key, null);
else
foreach (var str in value)
collection.Add(key, str);
}
public bool ContainsKey(string key) { return collection.GetValues(key) != null; }
public ICollection<string> Keys { get { return collection.AllKeys; } }
public bool Remove(string key)
{
bool found = ContainsKey(key);
if (found)
collection.Remove(key);
return found;
}
public bool TryGetValue(string key, out string[] value)
{
return (value = collection.GetValues(key)) != null;
}
public ICollection<string[]> Values
{
get
{
return new ReadOnlyCollectionAdapter<KeyValuePair<string, string[]>, string[]>(this, p => p.Value);
}
}
public string[] this[string key]
{
get
{
var value = collection.GetValues(key);
if (value == null)
throw new KeyNotFoundException(key);
return value;
}
set
{
Remove(key);
Add(key, value);
}
}
#endregion
#region ICollection<KeyValuePair<string,string[]>> Members
public void Add(KeyValuePair<string, string[]> item) { Add(item.Key, item.Value); }
public void Clear() { collection.Clear(); }
public bool Contains(KeyValuePair<string, string[]> item)
{
string[] value;
if (!TryGetValue(item.Key, out value))
return false;
return EqualityComparer<string[]>.Default.Equals(item.Value, value); // Consistent with Dictionary<TKey, TValue>
}
public void CopyTo(KeyValuePair<string, string[]>[] array, int arrayIndex)
{
foreach (var item in this)
array[arrayIndex++] = item;
}
public int Count { get { return collection.Count; } }
public bool IsReadOnly { get { return false; } }
public bool Remove(KeyValuePair<string, string[]> item)
{
if (Contains(item))
return Remove(item.Key);
return false;
}
#endregion
#region IEnumerable<KeyValuePair<string,string[]>> Members
public IEnumerator<KeyValuePair<string, string[]>> GetEnumerator()
{
foreach (string key in collection)
yield return new KeyValuePair<string, string[]>(key, collection.GetValues(key));
}
#endregion
#region IEnumerable Members
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); }
#endregion
}
public static class NameValueCollectionExtensions
{
public static NameValueCollectionDictionaryAdapter<TNameValueCollection> ToDictionaryAdapter<TNameValueCollection>(this TNameValueCollection collection)
where TNameValueCollection : NameValueCollection, new()
{
if (collection == null)
throw new ArgumentNullException();
return new NameValueCollectionDictionaryAdapter<TNameValueCollection>(collection);
}
}
public class ReadOnlyCollectionAdapter<TIn, TOut> : CollectionAdapterBase<TIn, TOut, ICollection<TIn>>
{
public ReadOnlyCollectionAdapter(ICollection<TIn> collection, Func<TIn, TOut> toOuter)
: base(() => collection, toOuter)
{
}
public override void Add(TOut item) { throw new NotImplementedException(); }
public override void Clear() { throw new NotImplementedException(); }
public override bool IsReadOnly { get { return true; } }
public override bool Remove(TOut item) { throw new NotImplementedException(); }
}
public abstract class CollectionAdapterBase<TIn, TOut, TCollection> : ICollection<TOut>
where TCollection : ICollection<TIn>
{
readonly Func<TCollection> getCollection;
readonly Func<TIn, TOut> toOuter;
public CollectionAdapterBase(Func<TCollection> getCollection, Func<TIn, TOut> toOuter)
{
if (getCollection == null || toOuter == null)
throw new ArgumentNullException();
this.getCollection = getCollection;
this.toOuter = toOuter;
}
protected TCollection Collection { get { return getCollection(); } }
protected TOut ToOuter(TIn inner) { return toOuter(inner); }
#region ICollection<TOut> Members
public abstract void Add(TOut item);
public abstract void Clear();
public virtual bool Contains(TOut item)
{
var comparer = EqualityComparer<TOut>.Default;
foreach (var member in Collection)
if (comparer.Equals(item, ToOuter(member)))
return true;
return false;
}
public void CopyTo(TOut[] array, int arrayIndex)
{
foreach (var item in this)
array[arrayIndex++] = item;
}
public int Count { get { return Collection.Count; } }
public abstract bool IsReadOnly { get; }
public abstract bool Remove(TOut item);
#endregion
#region IEnumerable<TOut> Members
public IEnumerator<TOut> GetEnumerator()
{
foreach (var item in Collection)
yield return ToOuter(item);
}
#endregion
#region IEnumerable Members
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
#endregion
}
Then an adapted can be constructed for a given NameValueCollection Data simply by doing:
var adapter = Data.ToDictionaryAdapter();
Notes:
Using the adapter may be be more performant than simply creating a copied dictionary, and should work well with any serializer that fully supports dictionary serialization.
The adapter might also be useful in using a NameValueCollection with any other code that expects an IDictionary of some sort - this is the fundamental advantage of the adapter pattern.
That being said, JavaScriptSerializer cannot be used with the adapter because this serializer cannot serialize an arbitrary type implementing IDictionary<TKey, TValue> that does not also inherit from Dictionary<TKey, TValue>. For details see Serializing dictionaries with JavaScriptSerializer.
When using DataContractJsonSerializer, a NameValueCollection can be replaced with an adapter in the serialization graph by using the data contract surrogate mechanism.
When using Json.NET a NameValueCollection can be replaced with an adapter using a custom JsonConverter such as the following:
public class NameValueJsonConverter<TNameValueCollection> : JsonConverter
where TNameValueCollection : NameValueCollection, new()
{
public override bool CanConvert(Type objectType)
{
return typeof(TNameValueCollection).IsAssignableFrom(objectType);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.SkipComments().TokenType == JsonToken.Null)
return null;
// Reuse the existing NameValueCollection if present
var collection = (TNameValueCollection)existingValue ?? new TNameValueCollection();
var dictionaryWrapper = collection.ToDictionaryAdapter();
serializer.Populate(reader, dictionaryWrapper);
return collection;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var collection = (TNameValueCollection)value;
var dictionaryWrapper = new NameValueCollectionDictionaryAdapter<TNameValueCollection>(collection);
serializer.Serialize(writer, dictionaryWrapper);
}
}
public static partial class JsonExtensions
{
public static JsonReader SkipComments(this JsonReader reader)
{
while (reader.TokenType == JsonToken.Comment && reader.Read())
;
return reader;
}
}
Which could be used e.g. as follows:
string json = JsonConvert.SerializeObject(Data, Formatting.Indented, new NameValueJsonConverter<NameValueCollection>());
NameValueCollection supports all of the following
A null value for a given key;
Multiple values for a given key (in which case NameValueCollection.Item[String] returns a comma-separated list of values);
A single value containing an embedded comma (which cannot be distinguished from the case of multiple values when using NameValueCollection.Item[String]).
Thus the adapter must implement IDictionary<string, string[]> rather than IDictionary<string, string> and also take care to handle a null value array.
Sample fiddle (including some basic unit testing) here: https://dotnetfiddle.net/gVPSi7
I'm devising a template language. In it, there are 3 kinds of tokens: tags, directives, and variables. Each of these tokens have a name, and there's getting to be quite a few of them. They're extensible too.
To allow name reuse I want to add namespaces.
Right now all the variables are just stored in a dict. The key is the variable name, and the value is the variable value. That way I can quickly retrieve the value of a variable. However, supposing I want to allow dot-notation, namespace.variable, how can I store these variables, such that the namespace is optional? If the namespace is included the dict should only scan that namespace, if not, I guess it scans all namespaces.
Is there a container that will do this?
You should structure your symbol data internally as a dictionary of dictionary of string. The top level dictionary is for namespaces, and each dictionary below each namespace name is the container for all symbols in that namespace.
Looking up an unqualified symbol is simply a matter of looking for the symbol in each namespace in a particular order. In C# or Delphi, the order is determined by the order in which the namespaces are declared at the top of the source file, in reverse order of declaration (most recent is the first to be searched).
You can create your own implementation of IDictionary<string, object> instead of using the framework's Dictionary<TKey, TValue>.
Externally, there would be no change to the way you are consuming it.
Internally, it would consist of a Dictionary<string, Dictionary<string, object>>.
So, if your dictionary is asked for the value matching key "namespace.variable", internally it would split that string, get the Dictionary<string, Dictionary<string, object>> with key "namespace" and then return the value in that Dictionary<string, object> for key "variable."
To make the namespace optional, you have one entry where the key is string.Empty. Whether adding or getting items, any time a key is provided that does not contain ., you'll use the entry with key string.Empty.
My solution:
Class
public class NamespaceDictionary<T> : IDictionary<string, T>
{
private SortedDictionary<string, Dictionary<string, T>> _dict;
private const char _separator = '.';
public NamespaceDictionary()
{
_dict = new SortedDictionary<string, Dictionary<string, T>>();
}
public NamespaceDictionary(IEnumerable<KeyValuePair<string, T>> collection)
: this()
{
foreach (var item in collection)
Add(item);
}
#region Implementation of IEnumerable
public IEnumerator<KeyValuePair<string, T>> GetEnumerator()
{
return _dict.SelectMany(x => x.Value).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
private static Tuple<string, string> Split(string name)
{
int pos = name.LastIndexOf(_separator);
string ns = pos == -1 ? "" : name.Substring(0, pos);
string var = name.Substring(pos + 1);
return new Tuple<string, string>(ns, var);
}
#region Implementation of ICollection<KeyValuePair<string,TValue>>
public void Add(KeyValuePair<string, T> item)
{
Add(item.Key, item.Value);
}
public void Clear()
{
_dict.Clear();
}
public bool Contains(KeyValuePair<string, T> item)
{
throw new NotImplementedException();
}
public void CopyTo(KeyValuePair<string, T>[] array, int arrayIndex)
{
throw new NotImplementedException();
}
public bool Remove(KeyValuePair<string, T> item)
{
return Remove(item.Key);
}
public int Count
{
get { return _dict.Sum(p => p.Value.Count); }
}
public bool IsReadOnly
{
get { return false; }
}
#endregion
#region Implementation of IDictionary<string,TValue>
public bool ContainsKey(string name)
{
var tuple = Split(name);
return ContainsKey(tuple.Item1, tuple.Item2);
}
public bool ContainsKey(string ns, string key)
{
if (ns == "")
return _dict.Any(pair => pair.Value.ContainsKey(key));
return _dict.ContainsKey(ns) && _dict[ns].ContainsKey(key);
}
public void Add(string name, T value)
{
var tuple = Split(name);
Add(tuple.Item1, tuple.Item2, value);
}
public void Add(string ns, string key, T value)
{
if (!_dict.ContainsKey(ns))
_dict[ns] = new Dictionary<string, T>();
_dict[ns].Add(key, value);
}
public bool Remove(string ns, string key)
{
if (_dict.ContainsKey(ns) && _dict[ns].ContainsKey(key))
{
if (_dict[ns].Count == 1) _dict.Remove(ns);
else _dict[ns].Remove(key);
return true;
}
return false;
}
public bool Remove(string key)
{
var tuple = Split(key);
return Remove(tuple.Item1, tuple.Item2);
}
public bool TryGetValue(string name, out T value)
{
var tuple = Split(name);
return TryGetValue(tuple.Item1, tuple.Item2, out value);
}
public bool TryGetValue(string ns, string key, out T value)
{
if (ns == "")
{
foreach (var pair in _dict)
{
if (pair.Value.ContainsKey(key))
{
value = pair.Value[key];
return true;
}
}
}
else if (_dict.ContainsKey(ns) && _dict[ns].ContainsKey(key))
{
value = _dict[ns][key];
return true;
}
value = default(T);
return false;
}
public T this[string ns, string key]
{
get
{
if (ns == "")
{
foreach (var pair in _dict)
if (pair.Value.ContainsKey(key))
return pair.Value[key];
}
else if (_dict.ContainsKey(ns) && _dict[ns].ContainsKey(key))
return _dict[ns][key];
throw new KeyNotFoundException();
}
set
{
if (!_dict.ContainsKey(ns))
_dict[ns] = new Dictionary<string, T>();
_dict[ns][key] = value;
}
}
public T this[string name]
{
get
{
var tuple = Split(name);
return this[tuple.Item1, tuple.Item2];
}
set
{
var tuple = Split(name);
this[tuple.Item1, tuple.Item2] = value;
}
}
public ICollection<string> Keys
{
get { return _dict.SelectMany(p => p.Value.Keys).ToArray(); }
}
public ICollection<T> Values
{
get { return _dict.SelectMany(p => p.Value.Values).ToArray(); }
}
#endregion
}
Test
var dict = new NamespaceDictionary<int>();
dict.Add("ns1.var1", 1);
dict.Add("ns2.var1", 2);
dict.Add("var2", 3);
dict.Add("ns2.var2", 4);
dict.Add("ns3", "var1", 5);
dict["ns4.var1"] = 6;
Console.WriteLine(dict["var1"]);
Console.WriteLine(dict["ns2.var1"]);
Console.WriteLine(dict["var2"]);
Console.WriteLine(dict["ns2.var2"]);
Console.WriteLine(dict["ns2", "var2"]);
Console.WriteLine(dict["ns3.var1"]);
Console.WriteLine(dict["ns4", "var1"]);
Output
1
2
3
4
4
5
6
Help
I used a SortedDictionary thinking it would retain the order that the namespaces were added, but it's actually sorting the namespaces alphabetically. Is there an dict class that will retain the order the items were added, but not sort them?