Comparing object used as Key in Dictionary - c#

my class:
public class myClass
{
public int A { get; set; }
public int B { get; set; }
public int C { get; set; }
public int D { get; set; }
}
and main example:
Dictionary<myClass, List<string>> dict = new Dictionary<myClass, List<string>>();
myClass first = new myClass();
first.A = 2;
first.B = 3;
myClass second = new myClass();
second.A = 2;
second.B = 3;
second.C = 5;
second.D = 6;
dict.Add(first, new List<string>());
if (dict.ContainsKey(second))
{
//
//should come here and update List<string> for first (and only in this example) key
//
}
else
{
//
//if myFirst object has difference vlues of A or B properties
//
dict.Add(second, new List<string>());
}
How to do this?

If you always want the dictionary only to compare on A and B, you have two options. Either use the constructor that implements IEqualityComparer<TKey> and put your comparison logic there, or have your class implement IEquateable<T> GetHashCode and Equals so the default comparer will give you the results you are looking for.
If you only want to compare on A and B in your one situation you will need to use the .Keys property and the Linq extension method Contains that allows you to pass in a IEqualityComparer<T>. However, when doing it this way you loose the speed benefits of using a Dictionary, so use it sparingly.
public class MyClassSpecialComparer : IEqualityComparer<myClass>
{
public bool Equals (myClass x, myClass y)
{
return x.A == y.A && x.B == y.B
}
public int GetHashCode(myClass x)
{
return x.A.GetHashCode() + x.B.GetHashCode();
}
}
//Special case for when you only want it to compare this one time
//NOTE: This will be much slower than a normal lookup.
var myClassSpecialComparer = new MyClassSpecialComparer();
Dictionary<myClass, List<string>> dict = new Dictionary<myClass, List<string>>();
//(Snip)
if (dict.Keys.Contains(second, myClassSpecialComparer ))
{
//
//should come here and update List<string> for first (and only in this example) key
//
}
//If you want it to always compare
Dictionary<myClass, List<string>> dict = new Dictionary<myClass, List<string>>(new MyClassSpecialComparer());

By default, comparison puts objects into buckets based on their hash code. A detailed comparison is then performed (by calling Equals) if two hash codes are the same. If your class neither provides GetHashCode or implements equality, the default object.GetHashCode will be used--in which case nothing specific to your class will be used for value comparison semantics. Only the same reference will be found. If you don't want this, implement GetHashCode and implement equality.
For example:
public class myClass
{
public int A { get; set; }
public int B { get; set; }
public int C { get; set; }
public int D { get; set; }
public bool Equals(myClass other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return other.A == A && other.B == B && other.C == C && other.D == D;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != typeof (myClass)) return false;
return Equals((myClass) obj);
}
public override int GetHashCode()
{
unchecked
{
int result = A;
result = (result*397) ^ B;
result = (result*397) ^ C;
result = (result*397) ^ D;
return result;
}
}
}

Override in your myClass:
GetHashCode method
Equals method
To implement GetHashCode method you can just XOR GetHashCodes from your integer properties.
Optionally override ToString method and implement IEquatable interface

Related

EqualityComparer on a nested object

I would like to compare two list of nested objects. If the parent objects Id differ and/or any of the childrens Id or Baz property differs, I want to consider them changed.
I've implemented my own version of Equals and GetHashCode below, but despite using my own equalitycomparer, Except() still yields a result, while I expect the objects to be equal.
var foo1 = new Foo
{
Id = 1,
Bars = new List<Bar>
{
new Bar
{
Id = 1,
Baz = 1.5
},
new Bar
{
Id = 1,
Baz = 1.5
}
}
};
var foo2 = new Foo
{
Id = 1,
Bars = new List<Bar>
{
new Bar
{
Id = 1,
Baz = 1.5
},
new Bar
{
Id = 1,
Baz = 1.5
}
}
};
var diff = new[] { foo1 }.Except(new[] { foo2 });
public class Foo
{
private sealed class IdBarsEqualityComparer : IEqualityComparer<Foo>
{
public bool Equals(Foo x, Foo y)
{
if (ReferenceEquals(x, y)) return true;
if (ReferenceEquals(x, null)) return false;
if (ReferenceEquals(y, null)) return false;
if (x.GetType() != y.GetType()) return false;
return x.Id == y.Id && Equals(x.Bars, y.Bars);
}
public int GetHashCode(Foo obj)
{
unchecked
{
return (obj.Id * 397) ^ (obj.Bars != null ? obj.Bars.GetHashCode() : 0);
}
}
}
public static IEqualityComparer<Foo> IdBarsComparer { get; } = new IdBarsEqualityComparer();
public int Id { get; set; }
public List<Bar> Bars { get; set; }
}
public class Bar
{
protected bool Equals(Bar other)
{
return Id == other.Id && Baz.Equals(other.Baz);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((Bar) obj);
}
public override int GetHashCode()
{
unchecked
{
return (Id * 397) ^ Baz.GetHashCode();
}
}
public int Id { get; set; }
public double Baz { get; set; }
}
There are three things wrong in your code:
You are not passing the equality comparer to Except method, so it's not being used.
Your GetHashCode implementation in Foo is wrong, it returns different results for same objects, so the Equals method is never called.
You are calling equals on two lists: Equals(x.Bars, y.Bars), this checks for reference equality. You can use SequenceEqual instead to compare elements one by one: x.Bars.SequenceEqual(y.Bars)

Aggregate duplicate records with Linq in polymorphic case

I would like to aggregate records stored in a List<>.
In the case I got a List<int>, the solution would be
var results = list.GroupBy(x => x).Select(g => g.Sum());
In the case I got a List<MyObject> with
public class MyObject
{
public MyObject(int pvalue)
{
Value = pvalue;
}
public int Value {get; set;}
public override bool Equals(object obj)
{
var p = obj as MyObject;
if (p == null)
return false;
return Value.Equals(p.Value);
}
public bool Equals(MyObject p)
{
if (p == null)
return false;
return Value.Equals(p.Value)
}
public override int GetHashCode()
{
int hash = 13;
hash = (hash * 7) + Value.GetHashCode();
return hash;
}
}
Then a solution would be :
myOjectList.GroupBy(x => x.Value).Select(g => new MyObject{ Value = g.Sum()});
Now in the case I got a List<IMyObject>, IMyObject being an interface (or an abstract class), and its concrete implementations got specific properties (example classes below), how can I solve the polymorphism in the Select of the previous Linq statement ?
public interface IMyObject
{
int Value {get; set;}
bool Equals(object obj);
int GetHashCode();
}
public class MyObject1 : IMyObject
{
public MyObject1(int pvalue, string pname)
{
Value = pvalue;
Name = pname;
}
public int Value {get; set;}
public string Name { get; set; }
public override bool Equals(object obj)
{
var p = obj as MyObject1;
if (p == null)
return false;
return Name.Equals(p.Name) && Value.Equals(p.Value);
}
public bool Equals(MyObject1 p)
{
if (p == null)
return false;
return Name.Equals(p.Name) && Value.Equals(p.Value)
}
public override int GetHashCode()
{
int hash = 13;
hash += (hash * 7) + Value.GetHashCode();
hash += (hash * 7) + Name.GetHashCode();
return hash;
}
}
public class MyObject2 : IMyObject
{
public MyObject2(int pvalue, int pvalue2)
{
Value = pvalue;
Value2 = pvalue2;
}
public int Value {get; set;}
public string Value2 { get; set; }
public override bool Equals(object obj)
{
var p = obj as MyObject1;
if (p == null)
return false;
return Value2.Equals(p.Value2) && Value.Equals(p.Value);
}
public bool Equals(MyObject1 p)
{
if (p == null)
return false;
return Value2.Equals(p.Value2) && Value.Equals(p.Value)
}
public override int GetHashCode()
{
int hash = 13;
hash += (hash * 7) + Value.GetHashCode();
hash += (hash * 7) + Value2.GetHashCode();
return hash;
}
}
Add a new operation to IMyObject to Clone the object:
public interface IMyObject {
int Value { get; set; }
bool Equals(object obj);
int GetHashCode();
IMyObject Clone();
}
Implement the Clone method:
public class MyObject1 : IMyObject {
...
public IMyObject Clone() {
return (IMyObject)this.MemberwiseClone();
}
}
public class MyObject2 : IMyObject {
...
public IMyObject Clone() {
return (IMyObject)this.MemberwiseClone();
}
}
Now you can use the Clone method to create an object to return (NOTE: you said nothing about how to pick the proper values for the other properties, so I arbitrarily used the first object in each group as the source).
var ans = myObjectList.GroupBy(x => x.Value).Select(g => { var rtnval = g.First().Clone(); rtnval.Value = g.Sum(m => m.Value); return rtnval; });
FIRST SOLUTION: REFLECTION
myListOfIMyObjects
.GroupBy(x => x.GetType())
.Select(x =>
{
var constr = x.Key.GetConstructor(Type.EmptyTypes);
var instance = (IMyObject)constr.Invoke(new object[0]);
instance.Value = x.Select(o => o.Value).Sum();
return instance;
})
.ToList();
Pro: you can embed all in a single Select, valid for all types implementing IMyObject, and you don't have to modify it if you add other classes like MyObject3 or MyObject4 later.
Con: Reflection is a fragile pattern, because you cannot rely on compilation checks. Also, all the MyObject classes must expose a parameterless constructor, and if it is not so, you will see an error only at runtime.
SECOND SOLUTION: OfType
var result1 = myListOfIMyObjects.OfType<MyObject1>();
var o1 = new MyObject1 { Value = result1.Sum(x => x.Value) };
var result2 = myListOfIMyObjects.OfType<MyObject2>();
var o2 = new MyObject2 { Value = result2.Sum(x => x.Value) };
var result = new List<IMyObject> { o1, o2 };
Pro: you have the compile-time checks, and the classes implementing IMyObject can have different constructors with different parameters.
Con: It's more verbose (you cannot embed that code in a single Select!), and if you add later other MyObject3, MyObject4 in your domain, you have to come back here and add other rows manually.

Distinct with encapsulated equality

I have a collection of objects where I want to find distinct values based on several properties.
I could do this:
var distinct = myValues.GroupBy(p => new { A = p.P1, B = p.P2 });
But I want to encapsulate the equality sementics. Something like this:
public interface IKey<T>
{
bool KeyEquals(T other);
}
public class MyClass : IKey<MyClass>
{
public string P1 { get; set; }
public string P2 { get; set; }
public bool KeyEquals(MyClass other)
{
if(object.ReferenceEquals(this, other)
return true;
if(other == null)
return false;
return this.P1 == other.P1 && this.P2 == other.P2;
}
}
Is there an O(N) way to get distinct values using my KeyEquals function?
If you can't change MyClass, you can implement an IEqualityComparer:
class MyClassComparer : IEqualityComparer<MyClass>
{
public bool Equals(MyClass m1, MyClass m2)
{
return m1.KeyEquals(m2);
}
public int GetHashCode(MyClass m)
{
return (m.P1.GetHashCode() *23 ) + (m.P2.GetHashCode() * 17);
}
}
And pass it to GroupBy
var distinct = myValues.GroupBy(p => p, new MyClassComparer());

List contains is always false C#

I have a structure (struct) with 2 fiels ID and Name. Then I created a List of this structure but anytime I do a contains it works for the first time adding it to the collection but then it's not working anymore. Why? It's not a reference is a struct. I want to validate if this isn't in the list add it.
public struct MyCar
{
public int id { get; set; }
public string name { get; set; }
}
List<MyCar> cars = new List<MyCar>();
MyCar myCar = new MyCar();
myCar.id = 1;
myCar.name = "a";
if(cars.Contains(myCar) == false)
{
cars.Add(myCar);
}
myCar = new MyCar();
myCar.id = 2;
myCar.name = "b";
if(cars.Contains(myCar) == false)
{
cars.Add(myCar);
}
myCar = new MyCar(); //Wrong. Duplicate and it's gonna be added again because Contains == false
myCar.id = 1;
myCar.name = "a";
if(cars.Contains(myCar) == false)
{
cars.Add(myCar);
}
Maybe I can use the Find to match for => X.ID and => X.NAME but I don't want this because my struct in fact is more complex that this two fields.
To use Contains class must override bool Equals(object obj) (and preferabley GetHashCode() too). Because you are using a struct instead of a class I would recomend implmenting IEquateable too;
public struct MyCar : IEquatable<MyCar>
{
public int id { get; set; }
public string name { get; set; }
private static readonly StringComparer stringComparer = StringComparer.Ordinal;
public override bool Equals(object obj)
{
if (obj is MyCar == false)
return false;
return Equals((MyCar)obj);
}
public bool Equals(MyCar car)
{
return this.id.Equals(car.id) && stringComparer.Equals(this.name,car.name);
}
public override int GetHashCode()
{
unchecked
{
int i = 17;
i = i * 23 + id.GetHashCode();
i = i * 23 + stringComparer.GetHashCode(name);
return i;
}
}
}
I'd have expected the Equals operator to be the answer, but when i test in a console app in .NET 4, i don't even get the error condition - probably coz the default ValueType.Equals compares all fields anyways. So where are you running this to get this error?
var a = new MyCar() { id = 1, name = "a" };
var b = new MyCar() { id = 1, name = "a" };
var x = new List<MyCar>();
x.Add(a);
Console.WriteLine(a.Equals(b));
Console.WriteLine(x.Contains(a));
Console.WriteLine(x.Contains(b)); //all 3 are true
Instead of using Contains you could use Any:
if (!cars.Any(m => m.id == myCar.id))
Now, if your heart is set on Contains, you'll need to implement IEquatable<MyCar> because Contains uses the default one; per MSDN Documentation:
This method determines equality by using the default equality comparer ...
public struct MyCar : IEquatable<MyCar>
{
public int id { get; set; }
public string name { get; set; }
public bool Equals(MyCar other)
{
return this.id == other.id;
}
public override bool Equals(object obj)
{
if (!(obj is MyCar)) { return false; }
return o.id == this.id;
}
public override int GetHashCode()
{
return this.id;
}
}
With that interface implemented you could now use Contains again. You need to think that through though because that means you're literally changing the default equality comparer for this type.

How to generate a unique hash for a collection of objects independent of their order [duplicate]

This question already has answers here:
Getting hash of a list of strings regardless of order
(5 answers)
Closed 8 years ago.
Let's say I have a class
public class MyClass
{
public string Type { get; set; }
public int Id { get; set; }
}
and I have a collection class that is simply a strongly typed List
public class MyClassList : List<MyClass>
{
public MyClassList(IEnumerable<MyClass> enumerable) : base (enumerable) {}
}
I want MyClassList to be able to generate a unique hash-code for MyClassList based on the contents. The hash-code of MyClass should be based on both properties. The hash-code of MyClassList should be the same even if the order of the objects is different.
To handle the ordering issue I was thinking I could order the list before generating the hash-code, but I'm not sure how to generate the hash-code of the list.
For optimal performance I would try to avoid iterating the whole collection every time GetHashCode is called. The purpose of GetHashCode is to improve performance to a point better than evaluating every element. So I might try maintaining the hash code when elements in the list are changed like this.
class Program
{
static void Main(string[] args)
{
MyClassList l = new MyClassList() { new MyClass() {Type="Bob", Id=1}, new MyClass() {Type="Jones", Id=2}};
MyClassList l2 = new MyClassList() { new MyClass() { Type = "Jones", Id = 2 }, new MyClass() { Type = "Bob", Id = 1 } };
MyClassList l3 = new MyClassList() { new MyClass() { Type = "Jones", Id = 2 }};
Console.WriteLine("{0} {1} {2}", l.GetHashCode(), l2.GetHashCode(), l3.GetHashCode());
l3.Add(new MyClass() { Type = "Bob", Id = 1 });
Console.WriteLine("{0}", l3.GetHashCode());
}
}
public class MyClass
{
public string Type { get; set; }
public int Id { get; set; }
public override int GetHashCode()
{
return (Type.GetHashCode() % 0x8000) | (int)((uint)Id.GetHashCode() & 0xFFFF0000);
}
}
public class MyClassList : IList<MyClass>
{
List<MyClass> internalList;
int hashCode = 0;
public MyClassList()
{
internalList = new List<MyClass>();
}
private void IncludeInHash(MyClass item)
{
hashCode ^= item.GetHashCode();
}
private void ExcludeFromHash(MyClass item)
{
IncludeInHash(item);
}
public override int GetHashCode()
{
return hashCode;
}
public int IndexOf(MyClass item)
{
return internalList.IndexOf(item);
}
public void Insert(int index, MyClass item)
{
internalList.Insert(index, item);
// Make sure Insert is successful (doesn't throw an exception) before affecting the hash
IncludeInHash(item);
}
public void RemoveAt(int index)
{
MyClass reduce = internalList[index];
internalList.RemoveAt(index);
// Make sure RemoveAt is successful before affecting the hash
ExcludeFromHash(reduce);
}
public MyClass this[int index]
{
get
{
return internalList[index];
}
set
{
MyClass reduce = internalList[index];
internalList[index] = value;
// Make sure these happen atomically; don't allow exceptions to prevent these from being accurate.
ExcludeFromHash(reduce);
IncludeInHash(value);
}
}
public void Add(MyClass item)
{
internalList.Add(item);
IncludeInHash(item);
}
public void Clear()
{
internalList.Clear();
hashCode = 0;
}
public bool Contains(MyClass item)
{
return internalList.Contains(item);
}
public void CopyTo(MyClass[] array, int arrayIndex)
{
internalList.CopyTo(array, arrayIndex);
}
public int Count
{
get { return internalList.Count; }
}
public bool IsReadOnly
{
get { return false; }
}
public bool Remove(MyClass item)
{
if (internalList.Remove(item))
{
ExcludeFromHash(item);
return true;
}
else
return false;
}
public IEnumerator<MyClass> GetEnumerator()
{
return internalList.AsReadOnly().GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
The solution given by clto works. Here is an alternative: sort the list by some total ordering (any ordering will do, as long as it is unambiguous). Then you can calculate the hash code using any normal means. You don't need order-independence. You could even use a cryptographic hash function.
I propose this solution (I didn't implement the Equals method) :
public class MyClass
{
public string Type { get; set; }
public int Id { get; set; }
public override int GetHashCode()
{
int hash = 17;
hash = hash + 23 * this.Type.GetHashCode();
hash = hash + 23 * this.Id.GetHashCode();
return hash;
}
}
public class MyClassList : List<MyClass>
{
public MyClassList(IEnumerable<MyClass> enumerable) : base(enumerable) { }
public override int GetHashCode()
{
return this.Aggregate(17, (state, current) => state * 23 + current.GetHashCode());
}
}
The way to generate the hashcode is inspired from Microsoft method to compute the hash value for anonymous objects.
If the order isn't important then you should use a collection that inherently is a set, rather than a list.
Also, it's generally best to not inherit from collections; use composition instead.
So for a collection you can use a HashSet, as it will have set semantics.
To have MyClass use both properties as it's identity just override it's equals and get hash code implementations, or create an IComparer<MyClass> if you can't or don't want to do that.
public class MyClass:IEquatable<MyClass>
{
public string Type { get; set; }
public int Id { get; set; }
public override bool Equals(object obj)
{
return Equals(obj as MyClass);
}
public bool Equals(MyClass other)
{
if (other == null)
return false;
return Type == other.Type &&
Id == other.Id;
}
public override int GetHashCode()
{
return Type.GetHashCode() * 79 + Id;
}
}
Then your collection is as simple as:
HashSet<MyClass> set = new HashSet<MyClass>();
And if you want to compare various sets just use:
HashSet<MyClass>.CreateSetComparer();

Categories

Resources