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);
Related
Not sure how to best phrase this which is probably why I'm having difficulty looking it up. Here is a sample console application to demonstrate my meaning.
class Program
{
static void Main(string[] args)
{
var item1 = new Item("Number");
var item2 = new Item("Number");
var dict = new Dictionary<Item, string>();
dict.Add(item1, "Value");
Console.WriteLine(dict.ContainsKey(item2));
var dict2 = new Dictionary<string, string>();
dict2.Add("Number", "Value");
Console.WriteLine(dict2.ContainsKey("Number"));
Console.Read();
}
class Item
{
readonly string number;
public Item(string number)
{
this.number = number;
}
}
}
In this example dict.ContainsKey(item2) returns false and dict2.ContainsKey("Number") returns true. Can Item be defined in such a way that it would behave like a string? The best I can come up with is
static void Main(string[] args)
{
var item1 = new Item("Number");
var item2 = new Item("Number");
var dict = new Dictionary<string, string>();
dict.Add(item1.ToString(), "Test");
Console.WriteLine(dict.ContainsKey(item2.ToString()));
Console.Read();
}
class Item
{
readonly string number;
public Item(string number)
{
this.number = number;
}
public override string ToString()
{
return number;
}
}
This example is contrived, Item would have more fields and ToString() would joint them all up.
You need to override Equals and GetHashCode. Dictionary use Equals and GetHashCode method to compare keys for equality.
class Item
{
readonly string number;
public Item(string number)
{
this.number = number;
}
public override bool Equals(object obj)
{
return Equals(obj as Item);
}
public override int GetHashCode()
{
// this is c# 6 feature
return number?.GetHashCode() ?? 0;
// If you are not using c# 6, you can use
// return number == null ? 0 : number.GetHashCode();
}
private bool Equals(Item another)
{
if (another == null)
return false;
return number == another.number;
}
}
If you have more than one field, you need to account all fields in the Equals and GetHashCode method.
Firstly I have seen IEqualityComparer for anonymous type and the answers there do not answer my question, for the obvious reason that I need an IEqualityComparer not and IComparer for use with Linq's Distinct() method. I have checked the other answers too and these fall short of a solution...
The Problem
I have some code to manipulate and pull records in from a DataTable
var glext = m_dtGLExt.AsEnumerable();
var cflist =
(from c in glext
orderby c.Field<string>(m_strpcCCType),
c.Field<string>(m_strpcCC),
c.Field<string>(m_strpcCCDesc),
c.Field<string>(m_strpcCostItem)
select new
{
CCType = c.Field<string>(m_strpcCCType),
CC = c.Field<string>(m_strpcCC),
CCDesc = c.Field<string>(m_strpcCCDesc),
CostItem = c.Field<string>(m_strpcCostItem)
}).Distinct();
but I need the distinct method to be case insensitive. What is throwing me here is the use of anonymous types.
Attempted Solution 1
If I had SomeClass which had concrete objects I could obviously do
public class SumObject
{
public string CCType { get; set; }
public string CC { get; set; }
public string CCDesc { get; set; }
public string CostItem { get; set; }
}
I could obviously do this
List<SumObject> lso = new List<SumObject>()
{
new SumObject() { CCType = "1-OCC", CC = "300401", CCDesc = "Rooney", CostItem = "I477" },
new SumObject() { CCType = "1-OCC", CC = "300401", CCDesc = "Zidane", CostItem = "I677" },
new SumObject() { CCType = "1-OCC", CC = "300401", CCDesc = "Falcao", CostItem = "I470" },
};
var e = lso.Distinct(new SumObjectComparer()); // Great :]
where
class SumObjectComparer : IEqualityComparer<SumObject>
{
public bool Equals(SumObject x, SumObject y)
{
if (Object.ReferenceEquals(x, y))
return true;
if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
return false;
return x.CCType.CompareNoCase(y.CCType) == 0 &&
x.CC.CompareNoCase(y.CC) == 0 &&
x.CCDesc.CompareNoCase(y.CCDesc) == 0 &&
x.CostItem.CompareNoCase(y.CostItem) == 0;
}
public int GetHashCode(SumObject o)
{
if (Object.ReferenceEquals(o, null))
return 0;
int hashCCType = String.IsNullOrEmpty(o.CCType) ?
0 : o.CCType.ToLower().GetHashCode();
int hashCC = String.IsNullOrEmpty(o.CC) ?
0 : o.CC.ToLower().GetHashCode();
int hashCCDesc = String.IsNullOrEmpty(o.CCDesc) ?
0 : o.CCDesc.ToLower().GetHashCode();
int hashCostItem = String.IsNullOrEmpty(o.CostItem) ?
0 : o.CostItem.ToLower().GetHashCode();
return hashCCType ^ hashCC ^ hashCCDesc ^ hashCostItem;
}
}
However, the use of anonymous types in the above Linq query are throwing me.
Attempted Solution 2
To attempt another solution to this (and because I have the same issue elsewhere) I generated the following generic comparer class
public class GenericEqualityComparer<T> : IEqualityComparer<T>
{
Func<T, T, bool> compareFunction;
Func<T, int> hashFunction;
public GenericEqualityComparer(Func<T, T, bool> compareFunction, Func<T, int> hashFunction)
{
this.compareFunction = compareFunction;
this.hashFunction = hashFunction;
}
public bool Equals(T x, T y) { return compareFunction(x, y); }
public int GetHashCode(T obj) { return hashFunction(obj); }
}
so that I could attempt to do
var comparer = new GenericEqualityComparer<dynamic>(
(x, y) => { /* My equality stuff */ },
o => { /* My hash stuff */ });
but this casts the returned value as IEnumerable<dynamic> which in turn effects my forthcoming use of cflist, so that in a following query the join fails.
var cf =
(from o in cflist
join od in glext
on new { o.CCType, o.CC, o.CCDesc, o.CostItem } equals new
{
CCType = od.Field<string>(m_strpcCCType),
CC = od.Field<string>(m_strpcCC),
CCDesc = od.Field<string>(m_strpcCCDesc),
CostItem = od.Field<string>(m_strpcCostItem)
}
into c
select new { ... }
I don't want to get into ugly casting to and from IEnumerable<T>s due to the heavy use of this code...
Question
Is there a way I can create my an IEquailityComparer for my anonymous types?
Thanks for your time.
Is there a way I can create my an IEquailityComparer for my anonymous types?
Sure. You just need to use type inference. For example, you could have something like:
public static class InferredEqualityComparer
{
public static IEqualityComparer<T> Create<T>(
IEnumerable<T> example,
Func<T, T, bool> equalityCheck,
Func<T, int> hashCodeProvider)
{
return new EqualityComparerImpl<T>(equalityCheck, hashCodeProvider);
}
private sealed class EqualityComparerImpl<T> : IEqualityComparer<T>
{
// Implement in the obvious way, remembering the delegates and
// calling them appropriately.
}
}
Then:
var glext = m_dtGLExt.AsEnumerable();
var query = from c in glext
orderby ...
select new { ... };
var comparer = InferredEqualityComparer.Create(query,
(x, y) => { ... },
o => { ... }
);
var distinct = query.Distinct(comparer);
Basically the first parameter to the method is just used for type inference, so that the compiler can work out what type to use for the lambda expression parameters.
You could create the comparer ahead of time by creating a sample of the anonymous type:
var sample = new[] { new { ... } };
var comparer = InferredExqualityComparer.Create(sample, ...);
var distinct = (... query here ... ).Distinct(comparer);
but then any time you change the query you've got to change the sample too.
This post may get what you want. Although for .NET 2.0 it also works for newer versions (see the bottom of this post for how to achieve this). In contrast to Jon Skeets solution we won´t use a factory-method like create. But this is only syntactic sugar I think.
I have a Key class contain some members. How would I filter a dictionary when I only have one member of the key object. Here is some code:
class Key
{
public int a { get; set; }
public int b { get; set; }
}
public class KeyEqualityComparer : IEqualityComparer<Key>
{
public int GetHashCode(Key k)
{
return (k.a + k.b).GetHashCode();
}
public bool Equals(Key lhs, Key rhs)
{
return ((lhs.a == rhs.a) && (lhs.b == rhs.b));
}
}
static Dictionary<Key, int> Data = new Dictionary<Key, int>( new KeyEqualityComparer() );
static void Main(string[] args)
{
Data.Add(new Key() { a = 0, b = 0 }, 99);
Data.Add(new Key() { a = 1, b = 0 }, 99);
// select all keys value pairs where the key contains a == 0
}
var myPairs = Data.Where(pair => pair.Key.a == 0);
If you're going to be doing multiple lookups by the same propert(ies) of the Key, you can use ToLookup to make this more efficient:
var myLookup = Data.ToLookup(pair => pair.Key.a);
var myPairs = myLookup[0];
Searching for a part of a composite key in the dictionary which doesn't use that value in its IEqualityComparer defeats the point of the dictionary. You might as well use a List.
I suggest you modify your KeyEqualityComparer to behave different when parts of the composite key are not provided.
Alternatively, if you intend to do many lookups this way and infrequent writes you may maintain multiple dictionaries having different equality comparers.
I need to arrange sort of dictionary where the key would be a pair of enum and int
and value is object. So I want to map a pair to some object.
One option would be
public enum SomeEnum
{
value1, value2
}
class Key
{
public SomeEnum;
public int counter;
// Do I have to implement Compare here?
}
Dictionary<SomeEnum, object> _myDictionary;
Another option would convert enum and int to some unique key.
string key = String.Format("{0}/{1}", enumValue, intValue)
That approach requires string parsing, a lot of extra work.
How to make it easily?
I would go with something similar to
public enum SomeEnum
{
value1, value2
}
public struct Key
{
public SomeEnum;
public int counter;
}
Dictionary<Key, object>
I think that would make it?
If you are going to put this in a dictionary, then you will need to make sure you implement a meaningful .Equals and .GetHashCode or the dictionary will not behave correctly.
I'd start off with something like the following for the basic compound key, and then implement a custom IComparer to get the sort order you need.
public class MyKey
{
private readonly SomeEnum enumeration;
private readonly int number;
public MyKey(SomeEnum enumeration, int number)
{
this.enumeration = enumeration;
this.number = number;
}
public int Number
{
get { return number; }
}
public SomeEnum Enumeration
{
get { return enumeration; }
}
public override int GetHashCode()
{
int hash = 23 * 37 + this.enumeration.GetHashCode();
hash = hash * 37 + this.number.GetHashCode();
return hash;
}
public override bool Equals(object obj)
{
var supplied = obj as MyKey;
if (supplied == null)
{
return false;
}
if (supplied.enumeration != this.enumeration)
{
return false;
}
if (supplied.number != this.number)
{
return false;
}
return true;
}
}
If you are using C# 4.0, you could use the Tuple class.
var key = Tuple.Create(SomeEnum.Value1, 3);
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.)