Collection of objects with two keys - c#

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

Related

Search Generic C# List with Contravariant Type

Imagine I have class like this:
class MyKey : IComparable<MyKey> {
public int Key { get; private set; }
public MyKey(int key) { Key = key; }
public int CompareTo(MyKey that) {
return that.Key - this.Key;
}
}
Furthermore, I have a generic wrapper class like this:
class MyListWrapper<T> where T : MyKey
{
private List<T> list;
public MyListWrapper(IEnumerable<T> items)
{
list = new List<T>(items);
list.Sort();
}
public int Search(T searchKey)
{
return list.BinarySearch(searchKey);
}
}
This allows people to store custom class inheriting from MyKey, and it works perfectly fine. However, it would also make sense to be able to search using a MyKey as we know T is a MyKey, and the list is sorted using MyKey's Key:
public int Search(MyKey searchKey)
{
return list.BinarySearch(searchKey); // Does not compile!
}
However, this doesn't compile, since BinarySearch takes a T (T could be any custom class).
Neither does it work, if I provide the comparer. Imagine MyKey wasn't comparable, but I made a custom comparer that used Key instead. I could use it when sorting and when searching.
Is is possible to search the list using MyKey somehow? I don't like storing the list as List<MyKey> and cast the values when I use them (that defeats the purpose of the generic list). I can't cast the list of type List<T> to List<MyKey> either.
You can create a wrapper class which inherits from MyNamedKey or create new instance of MyNamedKey itself just to search the item.
var mySearchKey = new MyKey { Key = 2 };
var index = list.BinarySearch(new MyNamedKeyWrapper(mySearchKey));
class MyNamedKeyWrapper : MyNamedKey
{
public MyNamedKeyWrapper(MyKey key)
{
this.Key = key.Key;
}
}
This will help you to maintain O(log n) while adding small allocation cost.
Or if you prefer to use brittle reflection, you can.. Get the instance of underlying array and cast it to MyKey[] (This works because arrays are covariant) and use Array.BinarySearch.
var array = (MyKey[])list.GetType()
.GetField("_items", BindingFlags.Instance | BindingFlags.NonPublic)
.GetValue(list);
var index = Array.BinarySearch(array, mySearchKey);
Edit: Since you don't know the most derived type, if you constraint new() with your generic parameter, you can achieve what you're looking for
class MyListWrapper<T> where T : MyKey, new()
{
private readonly List<T> list;
public MyListWrapper(IEnumerable<T> items)
{
list = new List<T>(items);
list.Sort();
}
public int Search(MyKey searchKey)
{
T dummyKey = new T() { Key = searchKey.Key };
return list.BinarySearch(dummyKey);
}
}
Linq is what you're after!
First, make sure you have System.Linq referenced in your usings.
Then you can use the following code to get all matching list items:
IEnumerable<MyNamedKey> found = list.Where(l => l.Key == 2);
To get a single item, use:
MyNamedKey found = list.FirstOrDefault(l => l.Key == 2);

Is there a way to validate values of a dictionary [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
C# dictionary type with unique keys and values
I would like to make sure that a dictionary has unique keys AND values. Is there any way to add this kind of validation outside of building my own class? That is the only way I can think of accomplishing validation for values in a dictionary. But, maybe there is some attribute I could add that I cannot seem to find via google.
I am looking to use this dictionary with WPF bindings, if that helps, also.
Dictionary keys are unique, by definition. Ensuring dictionary values are unique is done the same way you'd check every array or collection member is unique.
.NET 3.5 introduces HashSet<T> which speeds things up, assuming your TValue type implements Equals(TValue).
HashSet<TValue> seenValues = new HashSet<TValue>();
foreach(TKey key in myDictionary) {
if( seenValues .Contains( myDictionary[key] ) ) throw new Exception("Dictionary contains duplicate item.");
seenValues .Add( myDictionary[key] );
}
You could try using this two-way dictionary class:
public class Map<T1, T2>
{
private Dictionary<T1, T2> _forward = new Dictionary<T1, T2>();
private Dictionary<T2, T1> _reverse = new Dictionary<T2, T1>();
public Map()
{
this.Forward = new Indexer<T1, T2>(_forward);
this.Reverse = new Indexer<T2, T1>(_reverse);
}
public class Indexer<T3, T4>
{
private Dictionary<T3, T4> _dictionary;
public Indexer(Dictionary<T3, T4> dictionary)
{
_dictionary = dictionary;
}
public T4 this[T3 index]
{
get { return _dictionary[index]; }
set { _dictionary[index] = value; }
}
}
public void Add(T1 t1, T2 t2)
{
_forward.Add(t1, t2);
_reverse.Add(t2, t1);
}
public Indexer<T1, T2> Forward { get; private set; }
public Indexer<T2, T1> Reverse { get; private set; }
}
You can use it like this:
var map = new Map<int, string>();
map.Add(42, "Hello");
Console.WriteLine(map.Forward[42]);
// Outputs "Hello"
Console.WriteLine(map.Reverse["Hello"]);
//Outputs 42
It's a fairly simple implementation. You would probably need to expose some of the underlying dictionary functionality, but at least it is a start.

c# 2D auto expandable collection

I'm looking for a collection.
I need to be able to add elements as if using a 2D integer key, for example .Add(3, 4, element). If I add outside the range of the collection I need the collection to expand, this include negatively, although it can have a limit, for example the range of an Int16 would be good. Every element in the collection can have the same type as each other but I need to specify what that is, for example Set<type> s;
I also need to avoid slow operations such as searching when looking up an element, performance is less important when adding to the collection.
Does anyone have any ideas about what approach to use or best could provide the class in there answer.
If you want a compound key, you can use the Tuple<T1,T2> class in a : Dictionary<Tuple<T1,T2>, TItem>.
var coll = new Dictionary<Tuple<int,int>, AnyClass>();
coll.Add(new Tuple<int,int>(2, 3), new AnyClass("foo"));
coll.Add(new Tuple<int,int>(4, 2), new AnyClass("bar"));
var foo = coll[new Tuple<int,int>(2,3)];
var bar = coll[new Tuple<int,int>(4,2)];
If the syntax is too weird, you may wrap the class like this :
public class Dictionary2d<TKey1, TKey2, TItem> : Dictionary<Tuple<TKey1, TKey2>,TItem>
{
public void Add(TKey1 k1, TKey2, TItem item) {
this.Add(Tuple.Create(k1,k2), item);
}
public TItem this[TKey1 k1, TKey2 k2] {
get { return this[Tuple.Create(k1,k2)]; }
}
}
public class Program
{
static void Main() {
var coll = new Dictionary2d<int,int, AnyClass>();
coll.Add(2, 3, new AnyClass("foo"));
coll.Add(4, 2, new AnyClass("bar"));
var foo = coll[2,3];
var bar = coll[4,2];
}
}
The benefits of using Tuple class, is that the equality and hashcode comparison is natively handled, so even if it's a class, two differents instances of tuple with same values will be considered equals.
It sounds like you want a Dictionary<int, T>.
You can implement this Set<T> by storing its data in a private variable of type Dictionary<int, Dictionary<int, T>>.
You can then store using
public void Add(int key1, int key2, T value)
{
_storage[key1][key2] = value;
}

Multi-key dictionary with optional keys

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.

Ienumerable Extension method for dictionary array calls

Im trying to get an enumberable collection from a dictionary held array.
Or should I say, I'm trying to write an extension method for my dictionary objects which store arrays to return an IEnumerable item when the result is null.
Im using dictionarys to store array datasets (there are speed reasons for this), which I extract at certain search points. the extracted data is used in Linq queries, joins etc but I have problems when a data set doesnt exist.
Returning an empty (0 count) row set would fix my problem. what I have so far is this (simplified code ofcourse)
public class Supplier
{
public string ID {get;set}
public string Name {get;set}
}
private sups[] = new Supplier[10];
Dictionary<int,Supplier[]> dic = new Dictionary<int, Supplier[]>();
dic.Add(1,sups[]);
public static IEnumerable<Supplier> TryGetValue<Tkey>(this IDictionary<Tkey, Supplier[]> source, Tkey ItemKey)
{
Supplier[] foundList;
IEnumerable<Supplier> retVal;
if (source.TryGetValue(ItemKey, out foundList))
{
retVal = foundList.AsEnumerable();
}
else
{
retVal = new Supplier[0].AsEnumerable();
}
return retVal;
}
// later in the code there is some thing like:
dic.TryGetValue(1).Count()
//or a linq join
from a in anothertable
join d in dic.TryGetValue(1) on a.ID equals d.ID
What im trying to acheive is a generic extension method like below:
public static IEnumerable<T> TryGetValue<Tkey,TValue>(this IDictionary<Tkey, TValue> source, Tkey ItemKey)
{
// same code...
// returning retVal = new T[0].AsEnumerable();
}
I keep getting close but never exactly there.... I would like to keep the extension method parameters simple. Its the passing of T which keeps catching me out.
If anybody can help then please send me your feed back.
many thanks in advance!
EDIT: Complications with type-inference.
Here's a way, the idea is to constrain the dictionary-values' type to be anIEnumerableof something.
Unfortunately, type-inference doesn't seem to work with this signature (tested with C# 3), so you will have to specify the generic arguments explicitly.
public static IEnumerable<TUnderlyingValue> GetValueOrEmpty<TKey, TUnderlyingValue, TValue>
(this IDictionary<TKey, TValue> source, TKey key)
where TValue : IEnumerable<TUnderlyingValue>
{
if(source == null)
throw new ArgumentNullException("source");
TValue retVal;
return source.TryGetValue(key, out retVal) ? retVal : Enumerable.Empty<TUnderlyingValue>;
}
Usage:
var dict = new Dictionary<string, int[]>
{
{ "foo", new[] { 6, 7, 8 } }
{ "bar", new[] { 1 } }
};
var fooOrEmpty = dict.GetValueOrEmpty<string, int, int[]>("foo"); // { 6, 7, 8 }
var barOrEmpty = dict.GetValueOrEmpty<string, int, int[]>("bar"); // { 1 }
var bazOrEmpty = dict.GetValueOrEmpty<string, int, int[]>("baz"); // { }
Alternatively, we could use just 2 generic parameters without any constraints, but this will make the dictionary type less flexible. In this case, the compiler will infer the generic arguments just fine.
public static TUnderlyingValue[] GetValueOrEmpty<TKey, TUnderlyingValue>
(this IDictionary<TKey, TUnderlyingValue[]> source, TKey key)
{
if(source == null)
throw new ArgumentNullException("source");
TUnderlyingValue[] retVal;
return source.TryGetValue(key, out retVal) ? retVal : new TUnderlyingValue[0];
}

Categories