Multi-key dictionary with optional keys - c#

I have a need for a dictionary with multiple keys of 2 different types (int and string, both unique, so they can appear only inside of 1 key). Here is an example: group information (GroupInfo) can be queried by either GroupdId or one of the member names:
GroupId MemberNames GroupInfo
{1, John, Mary, Joe} ==> {GroupInfo}
So group info should be returned when requested by either id (1) or one of the member names (John).
My first solution was to create a key that wraps GroupdId and MemberNames with overridden Equals method that compares GroupIds and looks up a list of members. However to make these entries equal:
GroupId MemberNames
{0, John}
{1, null}
{1, Mary}
GetHashCode has to return the same const value. This will result in a dictionary becoming a linked list and performance degrading to O(N) lookup in the best case scenario.
The other solution is to keep 2 dictionaries separately: GroupId ==> GroupInfo, MemberName ==> GroupInfo.
Any other ideas?

Based on what you described in your comment
how'd you delete by a key? For example given a key "John" all other keys should be deleted as well.
It may have become clear to you now that a "Dictionary" isn't what you are looking for. Mostly because you have a need for multiple key types, and a need to map keys to other keys.
So you can create your own class that implements IDictionary. Basically as follows.
class MultiKeyDictionary : IDictionary
{
Dictionary<string, GroupInfo> stringDict = new Dictionary<string, GroupInfo>();
Dictionary<int, GroupInfo> intDict = new Dictionary<int, GroupInfo>();
Dictionary<GroupInfo, List<object>> keysDict = new Dictionary<GroupInfo, List<object>>();
//Each of these would add to their own dictionary, as well as adding the backwards
//entry in the "keysDict"
public void Add(string memberName, GroupInfo value);
public void Add(int key, GroupInfo value);
public bool Contains(string key);
public bool Contains(int key);
//This would be the enumerator of the "keys" of "keysDict"
//because it is actually a list of all GroupInfos
public IDictionaryEnumerator GetEnumerator()
public ICollection NameKeys;
public ICollection GroupIDKeys;
//This is to adhere to the interface. It should be carefully commented or even deprecated.
public ICollection Keys;
//For this, you look up the GroupInfo for the key, then do
//foreach(object key in keysDict[<groupInfoIJustLookedUp>]) {
// if(key.gettype == typeof(string) stringDict.Remove(key);
// else if (key.gettype == typeof(int) intDict.Remove(key);
// else //WHAT?!?
//}
public void Remove(string key);
public void Remove(int key);
//This would be the "Keys" collection of the "keysDict"
public ICollection Values;
//etc... etc...
public object this[string memberName];
public object this[int groupId];
}

In order to maintain only 1 dictionary, consider converting the GroupId (int) into a string and use it as a key (number 'keys' should not conflict with name keys). Maintain a references to the keys, so that if one gets deleted, the rest will be deleted.

Related

Collection of objects with two keys

Imagine this scenario: I need to manipulate (add, search and delete) items from a list of objects of type Book.
class Book{
int Id {get; set;}
string Title {get; set;}
string Author {get; set;}
int Year {get; set;}
// more properties
}
Constriants:
Id should be unique within the collection of Books
Title should be unique within the collection of Books
What I have so far, a Dictionary<int, Book> that has Id as a key and Book as a value. But in this case, If I want to add a new book to the dictionary I have to loop through all the values to check whether the Title is duplicate or not.
I start thinking about creating a HashSet only for Titles or having a second dictionary Dictionary<string, Book> that has Title as a key.
Any suggestion How to handle this scenario?
Edit:
As #David mentioned, I forgot to tell that my main concern here is performance. I want to lookup objects by Id and Title in the fastest way (O(1)).
You might use Tuple as the key:
var collection = new Dictionary<Tuple<int, string>, Book> (...);
var key = new Tuple<int, string>(1, "David"); // <<-----------
if(!collection.ContainsKey(key))
collection [key] = new Book(...);
Note that Tuple has its built in Equals() to make your life easier.
Update:
#AustinWBryan mentioned using ValueTuples (C# 7.0 feature) to replace Tuple, highly recommended. For more info about ValueTuples, refer to this link.
To ensure that both sides of the composite key are also unique a tuple won't cut it. Instead make your own key that checks for this in the equality checker.
public struct CompositeKey<T1, T2> : IEquatable<CompositeKey<T1, T2>>
{
private static readonly EqualityComparer<T1> t1Comparer = EqualityComparer<T1>.Default;
private static readonly EqualityComparer<T2> t2Comparer = EqualityComparer<T2>.Default;
public T1 Key1;
public T2 Key2;
public CompositeKey(T1 key1, T2 key2)
{
Key1 = key1;
Key2 = key2;
}
public override bool Equals(object obj) => obj is CompositeKey<T1, T2> && Equals((CompositeKey<T1, T2>)obj);
public bool Equals(CompositeKey<T1, T2> other)
{
return t1Comparer.Equals(Key1, other.Key1)
&& t2Comparer.Equals(Key2, other.Key2);
}
public override int GetHashCode() => Key1.GetHashCode();
}
So the dictionary works on buckets. It puts all the keys into buckets based on the hash code generated by GetHashCode(). Then it searches that bucket using a for loop over Equals(). The idea is that buckets should be as small as possible (ideally one item).
So we can control when a key will match, and how many buckets/items there are by controlling the hash code. If we return a constant hash code like 0, then everything is in the same bucket and it's down to the equality method to compare every item.
This comparer only returns the hash of the first key item. Assuming the first key item should be unique this is enough. Each bucket should still be one item, and when doing a lookup (that uses the full equals method) that's when the second key is also checked to ensure the type is the same value.
If you want to use ValueTuple as the key type you can pass in a custom comparer to the dictionary to achieve the same effect.
public class CompositeValueTupleComparer<T1, T2> : IEqualityComparer<(T1, T2)>
{
private static readonly EqualityComparer<T1> t1Comparer = EqualityComparer<T1>.Default;
private static readonly EqualityComparer<T2> t2Comparer = EqualityComparer<T2>.Default;
public bool Equals((T1, T2) x, (T1, T2) y) =>
t1Comparer.Equals(x.Item1, y.Item1) && t2Comparer.Equals(x.Item2, y.Item2);
public int GetHashCode((T1, T2) obj) => obj.Item1.GetHashCode();
}
new Dictionary<(int, string), Book>(new CompositeValueTupleComparer<int, string>());
It seems like both the ID and Name are going to be unique, as in, you shouldn't be able to use the same ID twice, regardless if the name has been used already. Otherwise, we'd end up with dict[3] referring to two different values.
Tuples or structs can't give that behavior, and still require you to loop. What you should instead do, is use a class similar to the one I've created:
public class TwoKeyDictionary<TKey1, TKey2, TValue>
{
public readonly List<TKey1> firstKeys = new List<TKey1>();
public readonly List<TKey2> secondKeys = new List<TKey2>();
public readonly List<TValue> values = new List<TValue>();
public void Add(TKey1 key1, TKey2 key2, TValue value)
{
if (firstKeys.Contains(key1)) throw new ArgumentException();
if (secondKeys.Contains(key2)) throw new ArgumentException();
firstKeys.Add(key1);
secondKeys.Add(key2);
values.Add(value);
}
public void Remove(TKey1 key) => RemoveAll(firstKeys.IndexOf(key));
public void Remove(TKey2 key) => RemoveAll(secondKeys.IndexOf(key));
private void RemoveAll(int index)
{
if (index < 1) return;
firstKeys.RemoveAt(index);
secondKeys.RemoveAt(index);
values.RemoveAt(index);
}
public TValue this[TKey1 key1]
{
get
{
int index = firstKeys.IndexOf(key1);
if (index < 0) throw new IndexOutOfRangeException();
return values[firstKeys.IndexOf(key1)];
}
}
public TValue this[TKey2 key2]
{
get
{
int index = secondKeys.IndexOf(key2);
if (index < 0) throw new IndexOutOfRangeException();
return values[secondKeys.IndexOf(key2)];
}
}
}
And then you can use it like this:
var twoDict = new TwoKeyDictionary<int, string, float>();
twoDict.Add(0, "a", 0.5f);
twoDict.Add(2, "b", 0.25f);
Console.WriteLine(twoDict[0]); // Prints "0.5"
Console.WriteLine(twoDict[2]); // Prints "0.25"
Console.WriteLine(twoDict["a"]); // Prints "0.5"
Console.WriteLine(twoDict["b"]); // Prints "0.25"
twoDict.Add(0, "d", 2); // Throws exception: 0 has already been added, even though "d" hasn't
twoDict.Add(1, "a", 5); // Throws exception: "a" has already been added, even though "1" hasn't
The TwoKeyDictionary would need to implement ICollection, IEnumerable, etc., to do the full behavior stuff

Data structure with key and value as class type and access value using both key and index

I am looking for a data structure in C# similar to Dictionary where i can access value by using index and key.The value is of type Class and not string.
I am creating a class library where i am trying to create a method named Item which returns value from my value and key pair data structure. But if i pass integer parameter to Item then it should get value by using index and if pass string type to Item then it should access value by using key (as in Dictionary)
Note: i referred this link . But System.Collections.Specialized.NameValueCollection cannot be used as it can only store strings as values, but i want class type as value. I want to access value using both key and index.
If a non-generic collection suits your needs, you could use OrderedDictionary. Expect to do a lot of boxing and unboxing if you work with value types though.
If you absolutely want a generic version, you will have to build your own. For instance:
public class OrderedDictionary<TKey, TValue>
{
private Dictionary<TKey, TValue> _innerDictionary = new Dictionary<TKey, TValue>();
private List<TKey> _keys = new List<TKey>();
public TValue this[TKey key]
{
get
{
return _innerDictionary[key];
}
set
{
if(!_innerDictionary.ContainsKey(key))
{
_keys.Add(key);
}
_innerDictionary[key] = value;
}
}
public TValue this[int index]
{
get
{
return _innerDictionary[_keys[index]];
}
set
{
_innerDictionary[_keys[index]] = value;
}
}
public void Add(TKey key, TValue value)
{
_innerDictionary.Add(key, value);
_keys.Add(key);
}
public void Clear()
{
_innerDictionary.Clear();
_keys.Clear();
}
public bool Remove(TKey key)
{
if (_innerDictionary.Remove(key))
{
_keys.Remove(key);
return true;
}
else
{
return false;
}
}
}
You will probably need to had some more exception handling, but most of the features are here. Also, if you want to implement IDictionary<TKey,TVale> from there, the missing members should be trivial.
System.Collections.Generic.Dictionary<TKeyItem, TValueItem> myDictionary =
new System.Collections.Generic.Dictionary<TKeyItem, TValueItem>();
var key = new TKeyItem(); //create key object
var val = new TValueItem(); //create value object
myDictionary.Add(key, val); //add key value pair to the dictionary
var valueByKey = myDictionary[key]; // it will return val by key
var valuebyIndex = myDictionary.Values.ElementAt(0); // it will retun val by index
Updated test to remove/add items to dictionary. It appears that removing and then adding a new item to the dictionary causes the new item to appear at the removed-item's position. Which makes my claim that using a Dictionary for this task will work false. I prefer to leave this answer in place. Even though it is not a valid solution it may demonstrate why the Dictionary solution should be not be used when wanting to access the collection by key as well as index.
using System;
using NUnit.Common;
using NUnit.Framework;
using System.Collections.Generic;
using System.Linq;
namespace UnitTests
{
[TestFixture]
public class UnitTest1
{
[Test]
public void TestMethod1()
{
var types = new[] { typeof(int), typeof(float), typeof(Guid), typeof(UnitTest1) };
Dictionary <Guid, object> dictionary = types.Select(x => Activator.CreateInstance(x)).ToDictionary(y => Guid.NewGuid());
types.ToList().ForEach(t =>
Assert.IsTrue(dictionary.Any(x => x.Value.GetType() == t))
);
Assert.IsTrue(dictionary.ElementAt(0).Value is int);
Assert.IsTrue(dictionary.ElementAt(1).Value is float);
Assert.IsTrue(dictionary.ElementAt(2).Value is Guid);
Assert.IsTrue(dictionary.ElementAt(3).Value is UnitTest1);
dictionary.Add(Guid.NewGuid(), Guid.NewGuid());
Assert.IsTrue(dictionary.ElementAt(4).Value is Guid);
//Remove the Guid
dictionary.Remove(dictionary.ElementAt(2).Key);
//See that the Element at the 2 position is now UnitTest1
Assert.IsTrue(dictionary.ElementAt(2).Value is UnitTest1);
//See that the Element at the 1 position is still float
Assert.IsTrue(dictionary.ElementAt(1).Value is float);
dictionary.Add(Guid.NewGuid(), new List<Guid>());
//See that a newly added item is put the end of the dictionary
Assert.IsTrue(dictionary.ElementAt(4).Value is List<Guid>); //This Assertion Fails. The List<Guid> appears at position 2.
}
}
}
Code above creates a dictionary of objects of various types and then finds them in the dictionary based on the type. Lastly we get the dictionary element at the 0 position and assert that it is an int. Note that the key of the dictionary is not used, and could have been any datatype.

Class design for read-only collection properties

I have an IDictionary<string, MyEnum?> collection that needs to be passed to a class to wrap it in a IReadOnlyDictionary<string, MyEnum> (note MyEnum but not MyEnum?).
I have come up with two designs:
Delay the wrapping to IReadOnlyDictionary<string, MyEnum> until property access:
public class MyClass
{
private readonly IEnumerable<KeyValuePair<string, MyEnum?>> _kvps;
public MyClass(IEnumerable<KeyValuePair<string, MyEnum?>> kvps)
{
_kvps = kvps;
}
public IReadOnlyDictionary<string, MyEnum> Kvps
{
get
{
var filtered = from kvp in _kvps
where kvp.Value.HasValue
select kvp;
return new ReadOnlyDictionary<string, MyEnum>(
filtered.ToDictionary(kvp => kvp.Key, kvp => (MyEnum)kvp.Value);
}
}
}
Eagerly evaluate the collection in constructor
public class MyClass
{
public MyClass(IEnumerable<KeyValuePair<string, MyEnum?>> kvps)
{
Kvps = ToReadOnly(kvps);
}
public IReadOnlyDictionary<string, MyEnum> Kvps { get; }
private static IReadOnlyDictionary<string, MyEnum> ToReadOnly(
IEnumerable<KeyValuePair<string, MyEnum?>> kvps)
{
var filtered = from kvp in kvps
where kvp.Value.HasValue
select kvp;
return new ReadOnlyDictionary<string, MyEnum>(
filtered.ToDictionary(kvp => kvp.Key, kvp => (MyEnum)kvp.Value);
}
}
The constructor design section of the Framework Design Guidelines suggests that minimal work should be done in constructors so I am opting for the first approach. However, that means every call to MyClass.Kvps will trigger a copy of _kvps which is not ideal.
I would like to know which is a better approach (or are there other ways) in terms of:
Memory efficiency (ideally only one copy of the collection is stored in MyClass)
Performance (property access should be fast and should not trigger a copy of the KeyValuePairs)
Out of the two requirements - don't copy the key value pairs and don't store two copies - you'll have to break one.
What causes us to look at this and think that there must be a solution is that we see TValue and TValue? and our minds want to see them as being of the same type. But they are not the same type.
It becomes clearer if you imagine that instead of TValue and TValue? that these are two different types, like an int and a string, and we want to project a collection of one to a collection of the other while filtering. For example,
List<string> GetStringsFromNonNegativeInts(List<int> ints)
{
return ints.Where(i=>i>-1).Select(i=>i.ToString()).ToList();
}
That's exactly the same scenario as trying to filter a set of TValue? to a set of TValue, even without the dictionary. It's just harder to see. TValue and TValue? code-blind us.
There are only two ways to do this. One is to copy each time, and the other is to keep two lists in synchronization.
EDIT: If you want the latest source values, best way is to implement your own class that implements IReadOnlyDictionary. Initialize this with a private field of ReadOnlyDictionary<string, MyEnum?>. Each call will do the lookup, and if the key exists AND HasValue, return the value.
Note that this implementation depends on the reference to the original values being passed in as an IReadOnlyDictionary to avoid having to copy values over.
public class MyReadOnlyDictionary<TKey, TValue> : IReadOnlyDictionary<TKey, TValue> where TValue : struct
{
// other methods to implement here...
public MyReadOnlyDictionary(IReadOnlyDictionary<TKey, TValue?> kvps)
{
_kvps = kvps;
}
private IReadOnlyDictionary<TKey, TValue?> _kvps;
new public TValue this[TKey key]
{
get
{
TValue? val = _kvps[key];
if (val.HasValue)
return val.Value;
throw new KeyNotFoundException();
}
}
}

How do I use a List<T> to reference a Dictionary values?

I'd like to have List<MyObject> myList reference Dictionary myDic values...I've tried
myList = myDic.Values.ToList()
but this seems to only copy the dictionary values. I need the list to reference the dictionary so any modifications (add, delete, update) made to myDic will be reflected in myList.
This is not really possible. myDic.Values is an IEnumerable<MyObject> that will enumerate the values in the dictionary. By calling .ToList() on it you are explicitly requesting that a copy be made. If you omit this call then you will have an enumerable that you can use instead, but of course you will be unable to index it or alter it directly.
Consider directly using the IEnumerable<MyObject> retrieved from myDic.Values instead -- what makes you think you need a List<MyObject>?
Of course you could use an ObservableDictionary instead of your list implementation
having the goodies of both worlds: which can be found here
If you need a list AND a dictionary and need to synchronize both of them without the drawback of having a fresh copy each time the dictionary changes, why wouldn't you write a class for that?
Here an short example, which is probably not suitable for threading issues:
public class DictRefList
{
private Dictionary<int, SomeRefClass> dict;
private List<SomeRefClass> list; // your list implementation here
public void DictRefList()
{
dict = new Dictionary<int, SomeRefClass>();
list = new List<SomeRefClass>;
}
public void Add(int key, SomeRefClass val)
{
dict.Add(key, val);
list.Add(val);
}
public void Delete(int key, SomeRefClass val)
{
dict.Remove(key);
list.Remove(val);
}
public void Update(int key, SomeRefClass val)
{
dict[key] = val;
list.Add(val); // do what is necessary to keep it valid and have no duplicate value
}
}

What could be used as a double sided Resource Dictionary?

I am using a ResourceDictionary, but I would like to be able to look up the value or the key with the other item. Each is always unique, so that is not a problem. Is there a type that has this double sided lookup feature?
Not built in, but this is pretty easy to write. I would probably implement IDictionary for this though... You would then dump the ResourceDictionary into your custom type.
public class DoubleLookup<TKey, TValue>
{
private IDictionary<TKey, TValue> keys;
private IDictionary<TValue, TKey> values;
//stuff...
public void Add(TKey key, TValue value)
{
this.keys.Add(key, value);
this.values.Add(value, key);
}
public TKey GetKeyFromValue(TValue value)
{
return this.values[value];
}
public TValue GetValueFromKey(TKey key)
{
return this.keys[key];
}
}
Be very careful when reversing the key/value relationship in a dictionary.
The contract of the dictionary guarantees that, for every value in the collection, there is exactly one key which maps to that value. The keys are unique. But the reverse is not true; for every distinct value, there can be many different keys mapping to that value.
In my own personal code library (written in Java, which is close enough), I have MultiMap class for just this kind of thing. Although the keys are unique, each key can be associated with multiple values. It's exactly identical to a Map>.
When I need to perform value-to-key lookups in a collection, I do something like this:
Map<K, V> lookupTable = ...;
MultiMap<V, K> reverseLookupTable = MapUtil.invert(lookupTable);
V value = ...;
if (reverseLookupTable.containsKey(value)) {
Set<K> keys = reverseLookupTable.get(value);
}
If you use something other than a MultiMap (like a HashMap or Dictionary) as your reverse-lookup table, you run the risk of losing some of your V->K mappings, unless you can guarantee that all keys AND all values in your collection are unique.
EDIT:
Oops. I just noticed that you said that all keys and values in your collection are unique. But I'll leave my answer here anyhow, as a warning for others reading this, who might not be able to provide the same guarantee.

Categories