How to attach a value with two keys - c#

I am using a IDictionary, but it allows one key for one value is there any way or IEnumerable list that I can use to add value with teo keys??
Thanks

you could use anything in the generic Dictionary as key...
for example:
class MyKey /*: IComparable*/ {
public string Key1 {get;set;}
public string Key2 {get;set;}
/* //CompareTo seems to be the wrong thing to implement...
public int CompareTo(object o) {
if(!(o is MyKey))
return -1;
int k1 = Key1.CompareTo(((MyKey)o).Key1);
return k1 == 0 ? Key2.CompareTo(((MyKey)o).Key2) : k1;
}*/
public override bool Equals(object o) {
return (o is MyKey) &&
(Key1 == ((MyKey)o).Key1) &&
(Key2 == ((MyKey)o).Key2);
}
public override int GetHashCode() {
return Key1.GetHashCode() ^ Key2.GetHashCode();
}
//to be very kewl we'll add the (in)equality-op's too...:
public static bool operator ==(MyKey m1, MyKey m2) {
return m1.Equals(m2);
}
public static bool operator !=(MyKey m1, MyKey m2) {
return !m1.Equals(m2);
}
}
Dictionary<MyKey, string> myKewlDictionary...

If you are looking for a way to generate a composite key from two values and you are using .NET 4.0 you can use a Tuple as a key - e.g.
var _myDictionary = new Dictionary<Tuple<int, int>, OtherClass>();
_myDictionary.Add(Tuple.Create(item1.Id, item2.Id), item3);
var item = _myDictionary[Tuple.Create(item1.Id, item2.Id)];

Hmm... Really, I don't know why you need that solution, it seems strange. Anyways, you can use custom IEnumerable class as your key collection.
You can find my test example below.
using System;
using System.Collections;
using System.Collections.Generic;
namespace TestApplication
{
class Program
{
static void Main(string[] args)
{
IDictionary<IEnumerable, object> dictionary1 = new Dictionary<IEnumerable, object>();
IEnumerable key11 = new string[] { "key1", "key2" };
IEnumerable key12 = new string[] { "key1", "key2" };
dictionary1.Add(key11, new object());
// Exception doesn't occur because key11 and key12 are not equal objects
dictionary1.Add(key12, new object());
IDictionary<KeyCollection<string>, object> dictionary2 = new Dictionary<KeyCollection<string>, object>();
KeyCollection<string> key21 = new KeyCollection<string>(new string[] { "key1", "key2" });
KeyCollection<string> key22 = new KeyCollection<string>(new string[] { "key1", "key2" });
dictionary2.Add(key21, new object());
// ArgumentEception: An item with the same key has already been added
dictionary2.Add(key22, new object());
}
private class KeyCollection<T> : IEnumerable where T : class
{
private IEnumerable<T> m_KeyCollection;
public KeyCollection() : this(new List<T>())
{
}
public KeyCollection(IEnumerable<T> array)
{
if (array == null)
{
throw (new NullReferenceException("'array' parameter must be initialized!"));
}
IList<T> list = new List<T>();
IEnumerator<T> enumerator = array.GetEnumerator();
while (enumerator.MoveNext())
{
list.Add(enumerator.Current);
}
m_KeyCollection = list;
}
public IEnumerator GetEnumerator()
{
return m_KeyCollection.GetEnumerator();
}
public override bool Equals(object obj)
{
KeyCollection<T> collection = (obj as KeyCollection<T>);
if (collection == null)
{
return false;
}
IEnumerator<T> enumerator1 = m_KeyCollection.GetEnumerator();
IEnumerator enumerator2 = collection.GetEnumerator();
bool moveNext1 = false, moveNext2 = false;
while (true)
{
moveNext1 = enumerator1.MoveNext();
moveNext2 = enumerator2.MoveNext();
if (moveNext1 && moveNext2)
{
T current1 = enumerator1.Current;
T current2 = (enumerator2.Current as T);
if ((current1 == null) || (current2 == null) || (!current1.Equals(current2)))
{
return false;
}
continue;
}
return ((!moveNext1) && (!moveNext2));
}
}
public override int GetHashCode()
{
IEnumerator<T> enumerator = m_KeyCollection.GetEnumerator();
string stringHash = string.Empty;
while (enumerator.MoveNext())
{
stringHash += string.Format("_{0}", ((enumerator.Current != null) ? enumerator.Current.GetHashCode().ToString() : "-1"));
}
return (string.IsNullOrEmpty(stringHash) ? -1 : stringHash.GetHashCode());
}
}
}
}

If your UserID and SessionID can never collide then you can use them both as hash keys: stuff your Userinfo in the dictionary once with the UserID, once with the SessionID, and in the cases when you only have one or the other, add to the dictionary with the only one you have.
(You may need to be concerned about adding a second key to a Userinfo object; say, if method login has a UserID and is deciding whether it needs to create a new Userinfo object to insert into the dictionary vs looking up a Userinfo object via the UserID, and method returning has already inserted the 'correct' Userinfo object into the dictionary using a SessionID but no UserID, login would incorrectly create a new Userinfo object. This may or may not be an issue for your application.)
If the UserID and SessionID can collide then you could use two dictionaries, and search them sequentially when needed. This might still be cleaner than using one dictionary with two different types of keys.
If you will always have one key and sometimes the other, you could use two different kinds of dictionaries: e.g., one to store UserID -> SessionID, and one for SessionID -> Userinfo; OR, one to store SessionID -> UserID, and one for UserID -> Userinfo. This would let you quickly chain your lookups based on the information available. (It feels more like a relational database.)

Related

C#: Easiest way to initialize & populate this particular 2D/3D Dictionary?

I have a bit of a complex dictionary.
It's a dictionary which holds two enumerated types & a List<>
Dictionary<BiomeType, Dictionary<LocationType, List<string>>> myDictionary;
So when I want to use it, I do something like this:
//Add "myString" to the List<string>
myDictionary[BiomeType.Jungle][LocationType.City1].Add("myString"));
When I try to add "myString" to myList, it throws an obvious & foreseeable error: "KeyNotFoundException: The given key was not present in the dictionary."
Is there any way in C# to automatically have the Dictionary add the Key if it isn't already there? I have a lot of BiomeTypes & even more LocationTypes. It would be a PITA to have to create each List, then create each locationtype dictionary, and then to add it for every BiomeType. All that work just to initialize this complex dictionary. Is there no easy way to do this?
I'm using this for gamedev, to store objects in a Dictionary, so I can access them by doing something like
BiomeType playerCurrentBiomeType;
LocationType playerCurrentLocationType;
LoadLevel(myDictionary[playerCurrentBiomeType][playerCurrentLocationType]);
//ex. myDictionary[BiomeType.Jungle][LocationType.CapitalCity]
//ex. myDictionary[BiomeType.Desert][LocationType.CapitalCity]
//ex. myDictionary[BiomeType.Desert][LocationType.City3]
Perhaps, you can try this:
Dictionary<BiomeType, Dictionary<LocationType, List<string>>> myDictionary = new Dictionary<BiomeType, Dictionary<LocationType, List<string>>>();
BiomeType playerCurrentBiomeType;
LocationType playerCurrentLocationType;
if(!myDictionary.ContainsKey(playerCurrentBiomeType))
{
myDictionary.Add(playerCurrentBiomeType, new Dictionary<LocationType , List<string>>{{playerCurrentLocationType, new List<string>()}});
}
myDictionary[playerCurrentBiomeType][playerCurrentLocationType].Add("bla");
You could do this (although to be honest I'm not sure you should!)
The class below is a type that generally acts like a dictionary, does what you asked for, and has some other changes to hide from you the empty items it creates every time you ask the indexer for an item that doesn't exist.
public class SmellyDictionary<T1, T2>: IDictionary<T1, T2>, ICollection where T2 : ICollection, new()
{
private readonly IDictionary<T1, T2> _dict = new Dictionary<T1, T2>();
public T2 this[T1 key]
{
get
{
T2 value;
if (!_dict.TryGetValue(key, out value))
_dict[key] = value = new T2(); // This stinks!
return value;
}
set { _dict[key] = value; }
}
public bool Contains(KeyValuePair<T1, T2> item)
{
return _dict.Contains(item);
}
public bool ContainsKey(T1 key)
{
return _dict.ContainsKey(key) && _dict[key].Count > 0; // This hides the smell
}
public int Count { get { return _dict.Count(kvp => kvp.Value.Count > 0); } } // This hides the smell
public void Add(T1 key, T2 value)
{
T2 currentValue;
if (_dict.TryGetValue(key, out currentValue) && currentValue.Count > 0)
throw new ArgumentException("A non empty element with the same key already exists in the SmellyDictionary");
_dict[key] = value;
}
public void Add(KeyValuePair<T1, T2> item)
{
Add(item.Key, item.Value);
}
public bool Remove(T1 key)
{
return _dict.Remove(key);
}
public bool Remove(KeyValuePair<T1, T2> item)
{
return _dict.Remove(item);
}
public bool TryGetValue(T1 key, out T2 value)
{
return _dict.TryGetValue(key, out value);
}
public ICollection<T1> Keys { get { return _dict.Keys; } }
public ICollection<T2> Values { get { return _dict.Values; } }
public object SyncRoot { get { return ((ICollection)_dict).SyncRoot; } }
public bool IsSynchronized { get { return ((ICollection)_dict).IsSynchronized; } }
public IEnumerator<KeyValuePair<T1, T2>> GetEnumerator()
{
return _dict.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public void Clear()
{
_dict.Clear();
}
public void CopyTo(Array array, int index)
{
_dict.CopyTo((KeyValuePair<T1, T2>[])array, index);
}
public void CopyTo(KeyValuePair<T1, T2>[] array, int arrayIndex)
{
_dict.CopyTo(array, arrayIndex);
}
public bool IsReadOnly { get { return _dict.IsReadOnly; } }
}
Here's a slightly more sensible option. Just call this method to add a string to your dictionary.
private void AddCityToDictionary(Dictionary<BiomeType, Dictionary<LocationType, List<string>>> myDictionary, BiomeType biome, LocationType location, string city)
{
Dictionary<LocationType, List<string>> locationDictionary;
if (!myDictionary.TryGetValue(biome, out locationDictionary))
locationDictionary = myDictionary[biome] = new Dictionary<LocationType, List<string>>();
List<string> cityList;
if (!locationDictionary.TryGetValue(location, out cityList))
cityList = locationDictionary[location] = new List<string>();
cityList.Add(city);
}
Simply looping through every possible enum type & adding in a new value works to fully populate this multi-dimensional dictionary.
Dictionary<BiomeType, Dictionary<LocationType, List<string>>> myDictionary = new Dictionary<BiomeType, Dictionary<LocationType, List<string>>>(); //No thanks to troll users like Peter.
foreach (BiomeType biomeType in System.Enum.GetValues(typeof(BiomeType)))
{
Dictionary<LocationType, List<string>> newLocDict = new Dictionary<LocationType, List<string>>(); //No thanks to troll users like Peter.
foreach (LocationType locType in System.Enum.GetValues(typeof(LocationType)))
{
List<string> newList = new List<string>();
newLocDict.Add(locType, newList); //Add the final bit here & voila! Finished! No thanks to troll users like Peter.
}
myDictionary.Add(biomeType, newLocDict);
}
Robyn's solution works the same way if you don't want to fully populate the container with ALL enum values.

Automatic dictionary key?

I kept googling for some time, and I found that the best way that enables you to have a list containing variables with a corresponding unique key is a HashTable or a Dictionary, but I didn't find anything that enables you to have automatic keys(of type integer). I want to call a function that adds an object(passed as a parameter) to the dictionary and returns the automatically generated key(int), and without any key duplicates. How could I accomplish this? I am completely struggling!
EDIT: To clarify things up. This is a server, and I want to assign a unique key for each client. If I use the maximum key value, this value will soon get to the int maximum value on large servers. Because if a client connects then disconnects he leaves behind an unused value which should be reused in order to avoid reaching a very high key maximum value.
The following should do and it reuses freed up keys:
internal class AutoKeyDictionary<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>>, IEnumerable
{
private readonly Dictionary<TKey, TValue> inner;
private readonly Func<TKey, TKey> incrementor;
private readonly Stack<TKey> freeKeys;
private readonly TKey keySeed;
private TKey currentKey;
public AutoKeyDictionary(TKey keySeed, Func<TKey, TKey> incrementor)
{
if (keySeed == null)
throw new ArgumentNullException("keySeed");
if (incrementor == null)
throw new ArgumentNullException("incrementor");
inner = new Dictionary<TKey, TValue>();
freeKeys = new Stack<TKey>();
currentKey = keySeed;
}
public TKey Add(TValue value) //returns the used key
{
TKey usedKey;
if (freeKeys.Count > 0)
{
usedKey = freeKeys.Pop();
inner.Add(usedKey, value);
}
else
{
usedKey = currentKey;
inner.Add(usedKey, value);
currentKey = incrementor(currentKey);
}
return usedKey;
}
public void Clear()
{
inner.Clear();
freeKeys.Clear();
currentKey = keySeed;
}
public bool Remove(TKey key)
{
if (inner.Remove(key))
{
if (inner.Count > 0)
{
freeKeys.Push(key);
}
else
{
freeKeys.Clear();
currentKey = keySeed;
}
return true;
}
return false;
}
public bool TryGetValue(TKey key, out TValue value) { return inner.TryGetValue(key, out value); }
public TValue this[TKey key] { get {return inner[key];} set{inner[key] = value;} }
public bool ContainsKey(TKey key) { return inner.ContainsKey(key); }
public bool ContainsValue(TValue value) { return inner.ContainsValue (value); }
public int Count { get{ return inner.Count; } }
public Dictionary<TKey,TValue>.KeyCollection Keys { get { return inner.Keys; } }
public Dictionary<TKey, TValue>.ValueCollection Values { get { return inner.Values; } }
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() { return inner.GetEnumerator(); }
IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)inner).GetEnumerator(); }
}
Disclaimer: I haven't tested this code, it could have a few pesty bugs of little importance, the general approach is sound.
Write a class which does this. Something like this:
class AutoIndexDictionary : IEnumerable<Whatever>
{
private readonly Dictionary<int, Whatever> myDict = new Dictionary<int, Whatever>();
private int currentIndex = 0;
public int Add(Whatever item)
{
var myIndex = currentIndex
myDict.Add(myIndex, item);
currentIndex ++;
return myIndex;
}
public void Remove(int index)
{
myDict.Remove(index);
}
// implement IEnumerable, indexer etc.
// ...
}
Create a method that gets the max key value from the dictionary using LINQ, adds 1 to it and then uses that as the key for the value you would like to add, like this:
public void AddToMyDictionary(string value)
{
int NextKey = MyDictionary.Keys.Max() + 1;
MyDictionary.Add(NextKey, value);
}
Obviously, this assumes your dictionary is a Dictionary<int, string>, but you can obviously modify for your purposes.
If you want to re-use keys that have been removed, store the next index when something is added / removed.
private int NextKey = 0;
public int AddToMyDictionary(string value)
{
int currentKey = NextKey;
MyDictionary.Add(currentKey, value);
NextKey = MyDictionary.Keys.Max() + 1;
return currentKey;
}
public void RemoveFromMyDictionary(int key)
{
MyDictionary.Remove(key);
NextKey = key;
}
This is what int Object.GetHashCode() is for.
Wouldn't a List do what you say, without any additional overhead? You call it a "unique integer key", but in List terminology, that's simply called an "index".
If you really wanted a custom function to add a value and get a key all in one step, you could inherit from List<T>, like so:
class MyCustomList<T> : List<T>
{
//Not thread-safe
public int AddAndGetKey(T valueToAdd)
{
Add(valueToAdd);
return LastIndexOf(valueToAdd);
}
}
I use LastIndexOf() because the list may include duplicate values and adding to the list always adds to the end. So this should work unless you get into multithreaded situations where you'd have to add-and-get-index in one atomic operation. (Alternately maybe you could add an extension method to List<T>.)
The advantage of using a List is that there would be no gaps in keys. On the flipside, removing an item in the middle would change the key of every item after it. But I guess it depends what behavior you're looking for.
Given the additional information provided in your edit then i don't think int is the correct datatype for you, you shouldn't reuse ID's the way you are describing as if a client with an ID gets disconnected but don't realise then you could have 1 ID in use by 2 clients. change your datatype to Guid then when you get a new client give it a key of Guid.NewGuid() and the chance of duplicate keys drops as close as possible to 0
I like Stefan Steinegger's solution. Here is an alternative that uses a List<> behind the scenes, but ensures the List<> is never removed from:
class AutoKeyDictionary<TValue> : IEnumerable<TValue> where TValue : class
{
readonly List<TValue> list = new List<TValue>();
public int Add(TValue val)
{
if (val == null)
throw new ArgumentNullException(nameof(val), "This collection will not allow null values.");
list.Add(val);
return list.Count - 1;
}
public void RemoveAt(int key)
{
// do not remove ('list.Count' must never decrease), overwrite with null
// (consider throwing if key was already removed)
list[key] = null;
}
public TValue this[int key]
{
get
{
var val = list[key];
if (val == null)
throw new ArgumentOutOfRangeException(nameof(key), "The value with that key is no longer in this collection.");
return val;
}
}
public int NextKey => list.Count;
public int Count => list.Count(v => v != null); // expensive O(n), Linq
public bool ContainsKey(int key) => key >= 0 && key < list.Count && list[key] != null;
public TValue TryGetValue(int key) => (key >= 0 && key < list.Count) ? list[key] : null;
public void Clear()
{
for (var i = 0; i < list.Count; ++i)
list[i] = null;
}
public IEnumerator<TValue> GetEnumerator() => list.Where(v => v != null).GetEnumerator(); // Linq
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public int FirstKeyOf(TValue val) => list.IndexOf(val);
public IDictionary<int, TValue> ToDictionary()
{
var retColl = new SortedList<int, TValue>(list.Count);
for (var i = 0; i < list.Count; ++i)
{
var val = list[i];
if (val != null)
retColl.Add(i, val);
}
return retColl;
}
// and so on...
}
Not thread-safe, obviously.
Be aware, the same value can be present several times in the collection, but with different keys.

UnitTesting List<T> of custom objects with List<S> of custom objects for equality

I'm writing some UnitTests for a parser and I'm stuck at comparing two List<T> where T is a class of my own, that contains another List<S>.
My UnitTest compares two lists and fails. The code in the UnitTest looks like this:
CollectionAssert.AreEqual(list1, list2, "failed");
I've written a test scenario that should clarify my question:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ComparerTest
{
class Program
{
static void Main(string[] args)
{
List<SimplifiedClass> persons = new List<SimplifiedClass>()
{
new SimplifiedClass()
{
FooBar = "Foo1",
Persons = new List<Person>()
{
new Person(){ ValueA = "Hello", ValueB="Hello"},
new Person(){ ValueA = "Hello2", ValueB="Hello2"},
}
}
};
List<SimplifiedClass> otherPersons = new List<SimplifiedClass>()
{
new SimplifiedClass()
{
FooBar = "Foo1",
Persons = new List<Person>()
{
new Person(){ ValueA = "Hello2", ValueB="Hello2"},
new Person(){ ValueA = "Hello", ValueB="Hello"},
}
}
};
// The goal is to ignore the order of both lists and their sub-lists.. just check if both lists contain the exact items (in the same amount). Basically ignore the order
// This is how I try to compare in my UnitTest:
//CollectionAssert.AreEqual(persons, otherPersons, "failed");
}
}
public class SimplifiedClass
{
public String FooBar { get; set; }
public List<Person> Persons { get; set; }
public override bool Equals(object obj)
{
if (obj == null) { return false;}
PersonComparer personComparer = new PersonComparer();
SimplifiedClass obj2 = (SimplifiedClass)obj;
return this.FooBar == obj2.FooBar && Enumerable.SequenceEqual(this.Persons, obj2.Persons, personComparer); // I think here is my problem
}
public override int GetHashCode()
{
return this.FooBar.GetHashCode() * 117 + this.Persons.GetHashCode();
}
}
public class Person
{
public String ValueA { get; set; }
public String ValueB { get; set; }
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
Person obj2 = (Person)obj;
return this.ValueA == obj2.ValueA && this.ValueB == obj2.ValueB;
}
public override int GetHashCode()
{
if (!String.IsNullOrEmpty(this.ValueA))
{
//return this.ValueA.GetHashCode() ^ this.ValueB.GetHashCode();
return this.ValueA.GetHashCode() * 117 + this.ValueB.GetHashCode();
}
else
{
return this.ValueB.GetHashCode();
}
}
}
public class PersonComparer : IEqualityComparer<Person>
{
public bool Equals(Person x, Person y)
{
if (x != null)
{
return x.Equals(y);
}
else
{
return y == null;
}
}
public int GetHashCode(Person obj)
{
return obj.GetHashCode();
}
}
}
The question is strongly related to C# Compare Lists with custom object but ignore order, but I can't find the difference, other than I wrap a list into another object and use the UnitTest one level above.
I've tried to use an IEqualityComparer:
public class PersonComparer : IEqualityComparer<Person>
{
public bool Equals(Person x, Person y)
{
if (x != null)
{
return x.Equals(y);
}
else
{
return y == null;
}
}
public int GetHashCode(Person obj)
{
return obj.GetHashCode();
}
}
Afterwards I've tried to implement the ''IComparable'' interface thats allows the objects to be ordered. (Basically like this: https://stackoverflow.com/a/4188041/225808)
However, I don't think my object can be brought into a natural order. Therefore I consider this a hack, if I come up with random ways to sort my class.
public class Person : IComparable<Person>
public int CompareTo(Person other)
{
if (this.GetHashCode() > other.GetHashCode()) return -1;
if (this.GetHashCode() == other.GetHashCode()) return 0;
return 1;
}
I hope I've made no mistakes while simplifying my problem. I think the main problems are:
How can I allow my custom objects to be comparable and define the equality in SimplifiedClass, that relies on the comparision of subclasses (e.g. Person in a list, like List<Person>). I assume Enumerable.SequenceEqual should be replaced with something else, but I don't know with what.
Is CollectionAssert.AreEqual the correct method in my UnitTest?
Equals on a List<T> will only check reference equality between the lists themselves, it does not attempt to look at the items in the list. And as you said you don't want to use SequenceEqual because you don't care about the ordering. In that case you should use CollectionAssert.AreEquivalent, it acts just like Enumerable.SequenceEqual however it does not care about the order of the two collections.
For a more general method that can be used in code it will be a little more complicated, here is a re-implemented version of what Microsoft is doing in their assert method.
public static class Helpers
{
public static bool IsEquivalent(this ICollection source, ICollection target)
{
//These 4 checks are just "shortcuts" so we may be able to return early with a result
// without having to do all the work of comparing every member.
if (source == null != (target == null))
return false; //If one is null and one is not, return false immediately.
if (object.ReferenceEquals((object)source, (object)target) || source == null)
return true; //If both point to the same reference or both are null (We validated that both are true or both are false last if statement) return true;
if (source.Count != target.Count)
return false; //If the counts are different return false;
if (source.Count == 0)
return true; //If the count is 0 there is nothing to compare, return true. (We validated both counts are the same last if statement).
int nullCount1;
int nullCount2;
//Count up the duplicates we see of each element.
Dictionary<object, int> elementCounts1 = GetElementCounts(source, out nullCount1);
Dictionary<object, int> elementCounts2 = GetElementCounts(target, out nullCount2);
//It checks the total number of null items in the collection.
if (nullCount2 != nullCount1)
{
//The count of nulls was different, return false.
return false;
}
else
{
//Go through each key and check that the duplicate count is the same for
// both dictionaries.
foreach (object key in elementCounts1.Keys)
{
int sourceCount;
int targetCount;
elementCounts1.TryGetValue(key, out sourceCount);
elementCounts2.TryGetValue(key, out targetCount);
if (sourceCount != targetCount)
{
//Count of duplicates for a element where different, return false.
return false;
}
}
//All elements matched, return true.
return true;
}
}
//Builds the dictionary out of the collection, this may be re-writeable to a ".GroupBy(" but I did not take the time to do it.
private static Dictionary<object, int> GetElementCounts(ICollection collection, out int nullCount)
{
Dictionary<object, int> dictionary = new Dictionary<object, int>();
nullCount = 0;
foreach (object key in (IEnumerable)collection)
{
if (key == null)
{
++nullCount;
}
else
{
int num;
dictionary.TryGetValue(key, out num);
++num;
dictionary[key] = num;
}
}
return dictionary;
}
}
What it does is it makes a dictionary out of the two collections, counting the duplicates and storing it as the value. It then compares the two dictionaries to make sure that the duplicate count matches for both sides. This lets you know that {1, 2, 2, 3} and {1, 2, 3, 3} are not equal where Enumerable.Execpt would tell you that they where.

Hash table with two primary keys

Using System.Collections how to create a collection with two primary keys ?
I mean new entries with the same combination are avoided but each key can be used with other keys (like combining two primary keys in SQL)
You can simply use a struct, example:
struct CompositeKey<T1,T2>
{
public T1 Item1;
public T2 Item2;
}
Then use that as the key.
You can use Tuple if you're using .NET 4.0.
Else you can create a Tuple by yourself.
Found on StackOverFlow : Tuples( or arrays ) as Dictionary keys in C#
struct Tuple<T, U, W> : IEquatable<Tuple<T,U,W>>
{
readonly T first;
readonly U second;
readonly W third;
public Tuple(T first, U second, W third)
{
this.first = first;
this.second = second;
this.third = third;
}
public T First { get { return first; } }
public U Second { get { return second; } }
public W Third { get { return third; } }
public override int GetHashCode()
{
return first.GetHashCode() ^ second.GetHashCode() ^ third.GetHashCode();
}
public override bool Equals(object obj)
{
if (obj == null || GetType() != obj.GetType())
{
return false;
}
return Equals((Tuple<T, U, W>)obj);
}
public bool Equals(Tuple<T, U, W> other)
{
return other.first.Equals(first) && other.second.Equals(second) && other.third.Equals(third);
}
}
Like LaGrandMere said, you can use System.Tuple if you're on .NET 4.0 or later:
Tuple<int,string> key = Tuple.Create(0, "Test");
Also, note that if you're putting strings, ints etc as keys in dictionaries you're going to have to special-case what would've been NULL in SQL. Can't have a null-key in a Dictionary.
var dict = new Dictionary<Tuple<string, int>, string>();
var address1 = Tuple.Create("5th Avenue",15);
var address2 = Tuple.Create("5th Avenue",25);
var address3 = Tuple.Create("Dag Hammarskjölds väg", 4);
dict[address1] = "Donald";
dict[address2] = "Bob";
dict[address3] = "Kalle";
// ...
int number = Int32.Parse("25");
var addressKey = Tuple.Create("5th Avenue",number);
string name = dict[addressKey]; // Bob
you can also construct composite key and use that in dictionary
var compositeKey = key1.ToString()+key2.ToString();
var dict = new Dictionary<string,object>();
dict.Add(compositekey,val);

Why doesn't Dictionary<TKey, TValue> support null key? [duplicate]

This question already has answers here:
Why can't you use null as a key for a Dictionary<bool?, string>?
(11 answers)
Need an IDictionary<TKey,TValue> implementation that will allow a null key
(8 answers)
Closed last year.
Firstly, why doesn't Dictionary<TKey, TValue> support a single null key?
Secondly, is there an existing dictionary-like collection that does?
I want to store an "empty" or "missing" or "default" System.Type, thought null would work well for this.
More specifically, I've written this class:
class Switch
{
private Dictionary<Type, Action<object>> _dict;
public Switch(params KeyValuePair<Type, Action<object>>[] cases)
{
_dict = new Dictionary<Type, Action<object>>(cases.Length);
foreach (var entry in cases)
_dict.Add(entry.Key, entry.Value);
}
public void Execute(object obj)
{
var type = obj.GetType();
if (_dict.ContainsKey(type))
_dict[type](obj);
}
public static void Execute(object obj, params KeyValuePair<Type, Action<object>>[] cases)
{
var type = obj.GetType();
foreach (var entry in cases)
{
if (entry.Key == null || type.IsAssignableFrom(entry.Key))
{
entry.Value(obj);
break;
}
}
}
public static KeyValuePair<Type, Action<object>> Case<T>(Action action)
{
return new KeyValuePair<Type, Action<object>>(typeof(T), x => action());
}
public static KeyValuePair<Type, Action<object>> Case<T>(Action<T> action)
{
return new KeyValuePair<Type, Action<object>>(typeof(T), x => action((T)x));
}
public static KeyValuePair<Type, Action<object>> Default(Action action)
{
return new KeyValuePair<Type, Action<object>>(null, x => action());
}
}
For switching on types. There are two ways to use it:
Statically. Just call Switch.Execute(yourObject, Switch.Case<YourType>(x => x.Action()))
Precompiled. Create a switch, and then use it later with switchInstance.Execute(yourObject)
Works great except when you try to add a default case to the "precompiled" version (null argument exception).
Why:
As described before, the problem is that Dictionary requires an implementation of the Object.GetHashCode() method. null does not have an implementation, therefore no hash code associated.
Solution: I have used a solution similar to a NullObject pattern using generics that enables you to use the dictionary seamlessly (no need for a different dictionary implementation).
You can use it like this:
var dict = new Dictionary<NullObject<Type>, string>();
dict[typeof(int)] = "int type";
dict[typeof(string)] = "string type";
dict[null] = "null type";
Assert.AreEqual("int type", dict[typeof(int)]);
Assert.AreEqual("string type", dict[typeof(string)]);
Assert.AreEqual("null type", dict[null]);
You just need to create this struct once in a lifetime :
public struct NullObject<T>
{
[DefaultValue(true)]
private bool isnull;// default property initializers are not supported for structs
private NullObject(T item, bool isnull) : this()
{
this.isnull = isnull;
this.Item = item;
}
public NullObject(T item) : this(item, item == null)
{
}
public static NullObject<T> Null()
{
return new NullObject<T>();
}
public T Item { get; private set; }
public bool IsNull()
{
return this.isnull;
}
public static implicit operator T(NullObject<T> nullObject)
{
return nullObject.Item;
}
public static implicit operator NullObject<T>(T item)
{
return new NullObject<T>(item);
}
public override string ToString()
{
return (Item != null) ? Item.ToString() : "NULL";
}
public override bool Equals(object obj)
{
if (obj == null)
return this.IsNull();
if (!(obj is NullObject<T>))
return false;
var no = (NullObject<T>)obj;
if (this.IsNull())
return no.IsNull();
if (no.IsNull())
return false;
return this.Item.Equals(no.Item);
}
public override int GetHashCode()
{
if (this.isnull)
return 0;
var result = Item.GetHashCode();
if (result >= 0)
result++;
return result;
}
}
It doesn't support it because the dictionary hashes the key to determine the index, which it can't do on a null value.
A quick fix would be to create a dummy class, and insert the key value ?? dummyClassInstance.
Would need more information about what you're actually trying to do to give a less 'hacky' fix
It just hit me that your best answer is probably to just keep track of whether a default case has been defined:
class Switch
{
private Dictionary<Type, Action<object>> _dict;
private Action<object> defaultCase;
public Switch(params KeyValuePair<Type, Action<object>>[] cases)
{
_dict = new Dictionary<Type, Action<object>>(cases.Length);
foreach (var entry in cases)
if (entry.Key == null)
defaultCase = entry.Value;
else
_dict.Add(entry.Key, entry.Value);
}
public void Execute(object obj)
{
var type = obj.GetType();
if (_dict.ContainsKey(type))
_dict[type](obj);
else if (defaultCase != null)
defaultCase(obj);
}
...
The whole rest of your class would remain untouched.
NameValueCollection could take null key.
If you really want a dictionary that allows null keys, here's my quick implementation (not well-written or well-tested):
class NullableDict<K, V> : IDictionary<K, V>
{
Dictionary<K, V> dict = new Dictionary<K, V>();
V nullValue = default(V);
bool hasNull = false;
public NullableDict()
{
}
public void Add(K key, V value)
{
if (key == null)
if (hasNull)
throw new ArgumentException("Duplicate key");
else
{
nullValue = value;
hasNull = true;
}
else
dict.Add(key, value);
}
public bool ContainsKey(K key)
{
if (key == null)
return hasNull;
return dict.ContainsKey(key);
}
public ICollection<K> Keys
{
get
{
if (!hasNull)
return dict.Keys;
List<K> keys = dict.Keys.ToList();
keys.Add(default(K));
return new ReadOnlyCollection<K>(keys);
}
}
public bool Remove(K key)
{
if (key != null)
return dict.Remove(key);
bool oldHasNull = hasNull;
hasNull = false;
return oldHasNull;
}
public bool TryGetValue(K key, out V value)
{
if (key != null)
return dict.TryGetValue(key, out value);
value = hasNull ? nullValue : default(V);
return hasNull;
}
public ICollection<V> Values
{
get
{
if (!hasNull)
return dict.Values;
List<V> values = dict.Values.ToList();
values.Add(nullValue);
return new ReadOnlyCollection<V>(values);
}
}
public V this[K key]
{
get
{
if (key == null)
if (hasNull)
return nullValue;
else
throw new KeyNotFoundException();
else
return dict[key];
}
set
{
if (key == null)
{
nullValue = value;
hasNull = true;
}
else
dict[key] = value;
}
}
public void Add(KeyValuePair<K, V> item)
{
Add(item.Key, item.Value);
}
public void Clear()
{
hasNull = false;
dict.Clear();
}
public bool Contains(KeyValuePair<K, V> item)
{
if (item.Key != null)
return ((ICollection<KeyValuePair<K, V>>)dict).Contains(item);
if (hasNull)
return EqualityComparer<V>.Default.Equals(nullValue, item.Value);
return false;
}
public void CopyTo(KeyValuePair<K, V>[] array, int arrayIndex)
{
((ICollection<KeyValuePair<K, V>>)dict).CopyTo(array, arrayIndex);
if (hasNull)
array[arrayIndex + dict.Count] = new KeyValuePair<K, V>(default(K), nullValue);
}
public int Count
{
get { return dict.Count + (hasNull ? 1 : 0); }
}
public bool IsReadOnly
{
get { return false; }
}
public bool Remove(KeyValuePair<K, V> item)
{
V value;
if (TryGetValue(item.Key, out value) && EqualityComparer<V>.Default.Equals(item.Value, value))
return Remove(item.Key);
return false;
}
public IEnumerator<KeyValuePair<K, V>> GetEnumerator()
{
if (!hasNull)
return dict.GetEnumerator();
else
return GetEnumeratorWithNull();
}
private IEnumerator<KeyValuePair<K, V>> GetEnumeratorWithNull()
{
yield return new KeyValuePair<K, V>(default(K), nullValue);
foreach (var kv in dict)
yield return kv;
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
NHibernate comes with a NullableDictionary. That did it for me.
https://github.com/nhibernate/nhibernate-core/blob/master/src/NHibernate/Util/NullableDictionary.cs
Dictionary will hash the key supplie to get the index , in case of null , hash function can not return a valid value that's why it does not support null in key.
In your case you are trying to use null as a sentinel value (a "default") instead of actually needing to store null as a value. Rather than go to the hassle of creating a dictionary that can accept null keys, why not just create your own sentinel value. This is a variation on the "null object pattern":
class Switch
{
private class DefaultClass { }
....
public void Execute(object obj)
{
var type = obj.GetType();
Action<object> value;
// first look for actual type
if (_dict.TryGetValue(type, out value) ||
// look for default
_dict.TryGetValue(typeof(DefaultClass), out value))
value(obj);
}
public static void Execute(object obj, params KeyValuePair<Type, Action<object>>[] cases)
{
var type = obj.GetType();
foreach (var entry in cases)
{
if (entry.Key == typeof(DefaultClass) || type.IsAssignableFrom(entry.Key))
{
entry.Value(obj);
break;
}
}
}
...
public static KeyValuePair<Type, Action<object>> Default(Action action)
{
return new KeyValuePair<Type, Action<object>>(new DefaultClass(), x => action());
}
}
Note that your first Execute function differs significantly from your second. It may be the case that you want something like this:
public void Execute(object obj)
{
Execute(obj, (IEnumerable<KeyValuePair<Type, Action<object>>>)_dict);
}
public static void Execute(object obj, params KeyValuePair<Type, Action<object>>[] cases)
{
Execute(obj, (IEnumerable<KeyValuePair<Type, Action<object>>>)cases);
}
public static void Execute(object obj, IEnumerable<KeyValuePair<Type, Action<object>>> cases)
{
var type = obj.GetType();
Action<object> defaultEntry = null;
foreach (var entry in cases)
{
if (entry.Key == typeof(DefaultClass))
defaultEntry = entry.Value;
if (type.IsAssignableFrom(entry.Key))
{
entry.Value(obj);
return;
}
}
if (defaultEntry != null)
defaultEntry(obj);
}
I come across this thread some days ago and needed a well thought out and clever solution to handle null keys. I took the time and implemented one by me to handle more scenarios.
You can find my implementation of NullableKeyDictionary currently in my pre-release package Teronis.NetStandard.Collections (0.1.7-alpha.37).
Implementation
public class NullableKeyDictionary<KeyType, ValueType> : INullableKeyDictionary<KeyType, ValueType>, IReadOnlyNullableKeyDictionary<KeyType, ValueType>, IReadOnlyCollection<KeyValuePair<INullableKey<KeyType>, ValueType>> where KeyType : notnull
public interface INullableKeyDictionary<KeyType, ValueType> : IDictionary<KeyType, ValueType>, IDictionary<NullableKey<KeyType>, ValueType> where KeyType : notnull
public interface IReadOnlyNullableKeyDictionary<KeyType, ValueType> : IReadOnlyDictionary<KeyType, ValueType>, IReadOnlyDictionary<NullableKey<KeyType>, ValueType> where KeyType : notnull
Usage (Excerpt of the Xunit test)
// Assign.
var dictionary = new NullableKeyDictionary<string, string>();
IDictionary<string, string> nonNullableDictionary = dictionary;
INullableKeyDictionary<string, string> nullableDictionary = dictionary;
// Assert.
dictionary.Add("value");
/// Assert.Empty does cast to IEnumerable, but our implementation of IEnumerable
/// returns an enumerator of type <see cref="KeyValuePair{NullableKey, TValue}"/>.
/// So we test on correct enumerator implementation wether it can move or not.
Assert.False(nonNullableDictionary.GetEnumerator().MoveNext());
Assert.NotEmpty(nullableDictionary);
Assert.Throws<ArgumentException>(() => dictionary.Add("value"));
Assert.True(dictionary.Remove());
Assert.Empty(nullableDictionary);
dictionary.Add("key", "value");
Assert.True(nonNullableDictionary.GetEnumerator().MoveNext());
Assert.NotEmpty(nullableDictionary);
Assert.Throws<ArgumentException>(() => dictionary.Add("key", "value"));
dictionary.Add("value");
Assert.Equal(1, nonNullableDictionary.Count);
Assert.Equal(2, nullableDictionary.Count);
The following overloads exists for Add(..):
void Add([AllowNull] KeyType key, ValueType value)
void Add(NullableKey<KeyType> key, [AllowNull] ValueType value)
void Add([AllowNull] ValueType value); // Shortcut for adding value with null key.
This class should behave same and intuitive as the dictionary does.
For Remove(..) keys you can use the following overloads:
void Remove([AllowNull] KeyType key)
void Remove(NullableKey<KeyType> key)
void Remove(); // Shortcut for removing value with null key.
The indexers do accept [AllowNull] KeyType or NullableKey<KeyType>. So supported scenarios, like they are stated in other posts, are supported:
var dict = new NullableKeyDictionary<Type, string>
dict[typeof(int)] = "int type";
dict[typeof(string)] = "string type";
dict[null] = "null type";
// Or:
dict[NullableKey<Type>.Null] = "null type";
I highly appreciate feedback and suggestions for improvements. :)
EDIT: Real answer to the question actually being asked: Why can't you use null as a key for a Dictionary<bool?, string>?
The reason the generic dictionary doesn't support null is because TKey might be a value type, which doesn't have null.
new Dictionary<int, string>[null] = "Null"; //error!
To get one that does, you could either use the non-generic Hashtable (which uses object keys and values), or roll your own with DictionaryBase.
Edit: just to clarify why null is illegal in this case, consider this generic method:
bool IsNull<T> (T value) {
return value == null;
}
But what happens when you call IsNull<int>(null)?
Argument '1': cannot convert from '<null>' to 'int'
You get a compiler error, since you can't convert null to an int. We can fix it, by saying that we only want nullable types:
bool IsNull<T> (T value) where T : class {
return value == null;
}
And, that's A-Okay. The restriction is that we can no longer call IsNull<int>, since int is not a class (nullable object)

Categories

Resources