List.Except not using custom Equals function - c#

public class eq : IEquatable<eq>
{
public string s1;
public string s2;
public override bool Equals(object obj)
{
if (obj == null) return false;
eq o = obj as eq;
if (o == null) return false;
return Equals(o);
}
public bool Equals(eq o)
{
if (s1==o.s1 && s2==o.s2)
return true;
return false;
}
public eq (string a,string b)
{
s1 = a;
s2 = b;
}
}
class Program
{
static void Main(string[] args)
{
List<eq> l1 = new List<eq>();
List<eq> l2 = new List<eq>();
l1.Add(new eq("1", "1"));
l1.Add(new eq("2", "2"));
l1.Add(new eq("3", "3"));
l2.Add(new eq("3", "3"));
l2.Add(new eq("1", "1"));
var b= l1.Contains(new eq("1", "1"));
var v = l1.Except(l2);
}
}
The l1.contains statement calls the custom function and returns the expected result
l1.Except does not result in a call to the custom function and returns the entire contents of l1
Is there a way to accomplish this without writing loops explicitly?

You should override GetHashCode method for Except to work properly. E.g
public override int GetHashCode()
{
int hash = 19;
hash = hash * 23 + (s1 == null) ? 0 : s1.GetHashCode();
hash = hash * 23 + (s2 == null) ? 0 : s2.GetHashCode();
return hash;
}
Except performs set operation (internally it fills Set<T> with items from second sequence and tries to add items from first sequence to same set).

var v = l1.Where(x => !l2.Contains(x));

Actually, Except (and Set<T>, Dictionary<K, T>) uses hash codes first, and only then (on hash codes conflict, when two hash codes are the same) - Equals, that's why whenever you redesign Equals you should implement GetHashCode as well. In your case:
public class eq : IEquatable<eq> {
public string s1;
public string s2;
// Your code could be shortened
public override bool Equals(object obj) {
return Equals(obj as eq);
}
public bool Equals(eq o) {
// Do not forget, that "o" can be null
if (Object.ReferenceEquals(null, o))
return false;
return String.Equals(s1, o.s1) && String.Equals(s2, o.s2);
}
public override int GetHashCode() {
// There's a possibility that either s1 or s2 are nulls
if (String.IsNullOrEmpty(s1))
if (String.IsNullOrEmpty(s2))
return 0;
else
return s2.GetHashCode();
else if (String.IsNullOrEmpty(s2))
return s1.GetHashCode();
// Typical trick: xoring hash codes
return s1.GetHashCode() ^ s2.GetHashCode();
}

Related

Distinct() not working as expected, and not calling Equals method, on a List of user defined objects

I have created my own definition of Tuple so that I can have my own definition of the Equals method, in which (1, 3) is equal to (3, 1). The Code is below.
public struct Tuple : IEquatable<object>
{
public int Item1 { get; set; }
public int Item2 { get; set; }
public Tuple(int item1, int item2)
{
this.Item1 = item1;
this.Item2 = item2;
}
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
if (Object.ReferenceEquals(this, obj))
return true;
Tuple other;
if (obj is Tuple)
{
other = (Tuple)obj;
}
else
{
return false;
}
if (this.Item1 == other.Item1 && this.Item2 == other.Item2
|| this.Item1 == other.Item2 && this.Item2 == other.Item1)
{
return true;
}
return false;
}
public override int GetHashCode()
{
int hash = 13;
hash = (hash * 7) + Item1.GetHashCode();
hash = (hash * 7) + Item2.GetHashCode();
return hash;
}
public static bool operator ==(Tuple left, Tuple right)
{
return left.Equals(right);
}
public static bool operator !=(Tuple left, Tuple right)
{
return !(left == right);
}
}
But when I run distinct on a List of Tuples, it does not use my definition of Equals. For example, on a vertices variable, like below:
List<Tuple> vertices
I do:
var distinctVertices = vertices.Distinct();
And it does check for distinct values, but it doesn't pass through my Equals definition, thus not working in the case I described above. Any idea on what I might be doing wrong?
Firstly, you probably want to implement IEquatable<Tuple>, not IEquatable<object> - Tuple : IEquatable<Tuple>.
Secondly, equality on a mutable struct is a terrible idea; I strongly suggest you use:
public readonly struct Tuple : IEquatable<Tuple>
{
public int Item1 { get; }
public int Item2 { get; }
public Tuple(int item1, int item2)
{
Item1 = item1;
Item2 = item2;
}
...
}
Thirdly, your Equals and GetHashCode must agree. If the order doesn't matter to Equals, it shouldn't matter to GetHashCode. Consider just:
public override int GetHashCode() => Item1 ^ Item2;
or something else that is not order dependent.
Finally,
your Equals can probably be simplfied:
public override bool Equals(object obj) => obj is Tuple other && Equals(other);
public bool Equals(Tuple other)
=> (Item1 == other.Item1 && Item2 == other.Item2)
|| (Item1 == other.Item2 && Item2 == other.Item1);
Take a look at this note in the Distinct documentation:
This method is implemented by using deferred execution. The immediate return value is an object that stores all the information that is required to perform the action. The query represented by this method is not executed until the object is enumerated either by calling its GetEnumerator method directly or by using foreach in Visual C# or For Each in Visual Basic.
This means that, until you enumerate the result, nothing will happen in terms of filtering the sequence.
If you do a foreach or adding a ToList on the result, i.e:
var distinctVertices = vertices.Distinct().ToList();
or
foreach(var vertice in distinctVertices)
{
// optionally do something here
}
Then you'll see your Equals method execute.

Checking list of objects for differences

I have the following two lists:
List<MyObject> oldList = new List<MyObject>();
oldList.Add(new MyObject { Id = 1, Name = "hello" });
oldList.Add(New MyObject { Id = 2, Name = "world" });
List<MyObject> newList = new List<MyObject>();
newList.Add(new MyObject { Id = 1, Name = "Hello" });
newList.Add(new MyObject { Id = 3, Name = "World" });
newList.Add(new MyObject { Id = 4, Name = "hello" });
I would like to write something to compare the two lists and return boolean true if the lists are different.
For example, the lists above are different in the following ways:
Id's don't exactly match
Counts don't match
In cases where Id's do match, the Name's don't exactly match (case-sensitive)
I have tried the following:
if (oldList != newList)
And:
if (!oldList.SequenceEqual(newList))
However, both produce inaccurate results. I understand I can create a class of type IEqualityComparer which implements a HashSet comparison; but I also read that it may not work for my case... Can someone shed any light on how to compare two objects to detect the types of changes I have specified?
As you've said, you only need to implement the correct IEquatable<MyObject> interface in your MyObject, or implement an IEqualityComparer<MyObject>
/// <summary>
/// Fully equatable MyObject
/// </summary>
public class MyObject : IEquatable<MyObject>
{
public int Id { get; set; }
public string Name { get; set; }
public override bool Equals(object obj)
{
// obj is object, so we can use its == operator
if (obj == null)
{
return false;
}
MyObject other = obj as MyObject;
if (object.ReferenceEquals(other, null))
{
return false;
}
return this.InnerEquals(other);
}
public bool Equals(MyObject other)
{
if (object.ReferenceEquals(other, null))
{
return false;
}
return this.InnerEquals(other);
}
private bool InnerEquals(MyObject other)
{
// Here we know that other != null;
if (object.ReferenceEquals(this, other))
{
return true;
}
return this.Id == other.Id && this.Name == other.Name;
}
public override int GetHashCode()
{
unchecked
{
// From http://stackoverflow.com/a/263416/613130
int hash = 17;
hash = hash * 23 + this.Id.GetHashCode();
hash = hash * 23 + (this.Name != null ? this.Name.GetHashCode() : 0);
return hash;
}
}
}
and then you can use
if (!oldList.SequenceEqual(newList))
Note that this will compare element order! If you change element order, then the comparison will return false
Or you can use an "external" IEqualityComparer<MyObject>
public class MyObjectEqualityComparer : IEqualityComparer<MyObject>
{
public static readonly MyObjectEqualityComparer Default = new MyObjectEqualityComparer();
protected MyObjectEqualityComparer()
{
}
public bool Equals(MyObject x, MyObject y)
{
if (object.ReferenceEquals(x, null))
{
return object.ReferenceEquals(y, null);
}
if (object.ReferenceEquals(y, null))
{
return false;
}
// Here we know that x != null && y != null;
if (object.ReferenceEquals(x, y))
{
return true;
}
return x.Id == y.Id && x.Name == y.Name;
}
public int GetHashCode(MyObject obj)
{
if (obj == null)
{
return 0;
}
unchecked
{
// From http://stackoverflow.com/a/263416/613130
int hash = 17;
hash = hash * 23 + obj.Id.GetHashCode();
hash = hash * 23 + (obj.Name != null ? obj.Name.GetHashCode() : 0);
return hash;
}
}
}
use it like
if (!oldList.SequenceEqual(newList, MyObjectEqualityComparer.Default))
Note that there are various schools of thought on how exactly to write equality comparers, and there is a little caveat: if you override the operator== you must be very very wary of not using it inside your comparator :-)
A classical example of wrong code, when the operator== is overloaded
public bool Equals(MyObject other)
{
// BOOOM!!! StackOverflowException!
// Equals will call operator== that will probably call
// Equals back! and so on and so on.
if (other == null)
{
return false;
}
return this.InnerEquals(other);
}
So it is better to do object.ReferenceEquals(something, someotherthing) that does reference comparison.
Then there is the problem of null handling with properties:
The hash of Name (a string) is written like this:
hash = hash * 23 + obj.Name != null ? obj.Name.GetHashCode() : 0
so that if obj.Name is null the code doesn't expode in a NullReferenceException. Automatically generated code for anonymous objects use another way:
hash = hash * 23 + EqualityComparer<string>.Default.GetHashCode(obj.Name);
The EqualityComparer<string>.Default is safe to use, even with null values.
For the Equals comparison of properties, the automatically generated code for anonymous objects uses another funny trick:
&& EqualityComparer<string>.Default.Equals(this.Name, obj.Name);
so it uses EqualityComparer<string>.Default.Equals, that then correctly uses the various methods/interfaces to compare objects.

Comparision of two list object for equality not giving proper result

I tried three ways of comparing two lists but not a single way is working.
static void Main(string[] args)
{
var list1 = new List<A>()
{
{new A(){Id = Guid.Parse("1BA3B3A3-FD4C-E311-B616-A41F729385FA")}},
{new A(){Id = Guid.Parse("90DF3989-16FC-4E2B-A0C7-A3640156D6F2")}}
};
var list2 = new List<A>()
{
{new A(){Id = Guid.Parse("1BA3B3A3-FD4C-E311-B616-A41F729385FA")}},
{new A(){Id = Guid.Parse("90DF3989-16FC-4E2B-A0C7-A3640156D6F2")}}
};
var test1 = ListEuality<A>(list1, list2, );
var areEquivalent = (list1.Count == list2.Count) && !list1.Except(list2).Any();
var test = list1.OrderBy(t => t.Id).SequenceEqual(list2.OrderBy(t => t.Id));
Console.ReadLine();
}
//This Method compares two lists for equality without taking consideration of order.
public static bool ListEuality<T>(IEnumerable<T> list1, IEnumerable<T> list2)
{
var cnt = new Dictionary<T, int>(comparer);
foreach (T s in list1)
{
if (cnt.ContainsKey(s))
{
cnt[s]++;
}
else
{
cnt.Add(s, 1);
}
}
foreach (T s in list2)
{
if (cnt.ContainsKey(s))
{
cnt[s]--;
}
else
{
return false;
}
}
//If dictionary element becomes 0 means dictionary modified for each item that means items are present in both list
return cnt.Values.All(c => c == 0);
}
class A
{
public Guid Id;
}
Probably all you need to do is to override the Equals method for your A object like so:
public override bool Equals(Object obj) {
if (obj == null) return false;
A objA = obj as A;
if (objA == null) return false;
return (this.Id.Equals(objA.Id));
}
This code:
void Main()
{
Guid g = Guid.Parse("1BA3B3A3-FD4C-E311-B616-A41F729385FA");
A a = new A();
a.Id = g;
Guid h = Guid.Parse("1BA3B3A3-FD4C-E311-B616-A41F729385FA");
A b = new A();
b.Id = h;
bool eq = a.Equals(b);
Console.WriteLine(eq);
}
// Define other methods and classes here
public class A {
public Guid Id;
public override bool Equals(Object obj) {
if (obj == null) return false;
A objA = obj as A;
if (objA == null) return false;
return (this.Id.Equals(objA.Id));
}
}
If you override Equals, it returns True. If you remove the Equals method for the A-class, it returns False.
The reason is that you are using different instances of A. A way to do it without overriding comparison for A would be
bool areEquivalent = (list1.Count == list2.Count) && !list1.Select(a => a.ID).Except(list2.Select(a => a.ID).Any();
Meaning you only compare the GUIDs

Class implementation of IEquatable for use as a key in a dictionary

I've got a class which consists of two strings and an enum. I'm trying to use instances of this class as keys in a dictionary. Unfortunately I don't seem to be implementing IEquatable properly. Here's how I've done it:
public enum CoinSide
{
Heads,
Tails
}
public class CoinDetails : IComparable, IEquatable<CoinDetails>
{
private string denomination;
private string design;
private CoinSide side;
//...
public int GetHashCode(CoinDetails obj)
{
return string.Concat(obj.Denomination, obj.Design, obj.Side.ToString()).GetHashCode();
}
public bool Equals(CoinDetails other)
{
return (this.Denomination == other.Denomination && this.Design == other.Design && this.Side == other.Side);
}
}
However, I still can't seem to look up items in my dictionary. Additionally, the following tests fail:
[TestMethod]
public void CoinDetailsHashCode()
{
CoinDetails a = new CoinDetails("1POUND", "1997", CoinSide.Heads);
CoinDetails b = new CoinDetails("1POUND", "1997", CoinSide.Heads);
Assert.AreEqual(a.GetHashCode(), b.GetHashCode());
}
[TestMethod]
public void CoinDetailsCompareForEquality()
{
CoinDetails a = new CoinDetails("1POUND", "1997", CoinSide.Heads);
CoinDetails b = new CoinDetails("1POUND", "1997", CoinSide.Heads);
Assert.AreEqual<CoinDetails>(a, b);
}
Would someone be able to point out where I'm going wrong? I'm sure I'm missing something rather simple, but I'm not sure what.
You class has to override Equals and GetHashCode:
public class CoinDetails
{
private string Denomination;
private string Design;
private CoinSide Side;
public override bool Equals(object obj)
{
CoinDetails c2 = obj as CoinDetails;
if (c2 == null)
return false;
return Denomination == c2.Denomination && Design == c2.Design;
}
public override int GetHashCode()
{
unchecked
{
int hash = 17;
hash = hash * 23 + (Denomination ?? "").GetHashCode();
hash = hash * 23 + (Design ?? "").GetHashCode();
return hash;
}
}
}
Note that i've also improved your GetHashCode algorithm according to: What is the best algorithm for an overridden System.Object.GetHashCode?
You could also pass a custom IEqualityComparer<CoinDetail> to the dictionary:
public class CoinComparer : IEqualityComparer<CoinDetails>
{
public bool Equals(CoinDetails x, CoinDetails y)
{
if (x == null || y == null) return false;
if(object.ReferenceEquals(x, y)) return true;
return x.Denomination == y.Denomination && x.Design == y.Design;
}
public int GetHashCode(CoinDetails obj)
{
unchecked
{
int hash = 17;
hash = hash * 23 + (obj.Denomination ?? "").GetHashCode();
hash = hash * 23 + (obj.Design ?? "").GetHashCode();
return hash;
}
}
}
Now this works and does not require CoinDetails to override Equals+GetHashCode:
var dict = new Dictionary<CoinDetails, string>(new CoinComparer());
dict.Add(new CoinDetails("1POUND", "1997"), "");
dict.Add(new CoinDetails("1POUND", "1997"), ""); // FAIL!!!!

Union/Except on lists with different object instances

Is it possible to perform union/except on Lists of Objects where the instance of objects are not necessarily the same but they are functionally equivalent?
What I mean is if I have a class like this,
Class A
{
String a;
int b;
double c;
}
And I had the following Lists:
A foo = new A() {"a",2,3.4}
A bar = new A() {"a",2,3.4}
List<A> firstList = new List<A>() { foo }
List<A> secondList = new List<A>() { bar }
How can I perform firstList.Except/Union on secondList if firstList and secondList had completely different object instances but the fields/properties of the objects are exactly the same?
You need to overload the Equals method of your class.
Right now, the way that it checks for equality is by checking the reference. There's a way to fix that, by overriding the Equals method:
class A
{
string a;
int b;
double c;
public override bool Equals(object obj)
{
A aobj = obj as A;
if (aobj == null) return false;
return a == aobj.a && b == aobj.b && c == aobj.c;
}
}
However, for these functions to perform at their best, you also need to override the GetHashCode method, too. Like this:
class A
{
string a;
int b;
double c;
public override bool Equals(object obj)
{
return a == obj.a && b == obj.b && c == obj.c;
}
public override int GetHashCode()
{
unchecked { return 17 * (a ?? "").GetHashCode() * b * c.GetHashCode(); }
}
}
Simply override the object.Equals method to tell the world when to treat your objects as equal. Your class A should look something like this:
class A
{
string a;
int b;
double c;
public override bool Equals(object obj)
{
if (!(obj is A)) return obj.Equals(this); // defer to other object
A other = (A)obj;
return a == other.a && b == other.b && c == other.c; // check field equality
}
public override int GetHashCode()
{
int hc = 13;
hc += a.GetHashCode() * 27;
hc += b.GetHashCode() * 27;
hc += c.GetHashCode() * 27;
}
}
Adding little more to previous answers. Overriding Equals will require overriding == and !=
public class A
{
String a;
int b;
double c;
public override bool Equals(object obj)
{
if (object.ReferenceEquals(null, obj))
{
return false;
}
if (object.ReferenceEquals(this, obj))
{
return true;
}
if (obj.GetType() != typeof(A))
{
return false;
}
var other = obj as A;
return string.Equals(this.a, other.a) && this.b == other.b && this.c == other.b;
}
public override int GetHashCode()
{
if (string.IsNullOrEmpty(a))
{
return this.b.GetHashCode() ^ this.c.GetHashCode();
}
return this.a.GetHashCode() ^ this.b.GetHashCode() ^ this.c.GetHashCode();
}
public static bool operator ==(A left, A right)
{
if (object.ReferenceEquals(left, right))
{
return true;
}
if (object.ReferenceEquals(null, left))
{
return false;
}
if (object.ReferenceEquals(null, right))
{
return false;
}
return left.Equals(right);
}
public static bool operator !=(A left, A right)
{
return !(left == right);
}
}
You can use LINQ to Objects to create intersections and unions on lists - and there is no need to override Equals and GetHashCode. Use the Enumerable.Intersect and Enumerable.Except methods:
public class A
{
public String a;
public int b;
public double c;
}
A foo = new A() {a = "a", b = 2, c = 3.4};
A bar = new A() {a = "a", b = 2, c = 3.4};
List<A> firstList = new List<A>() { foo };
List<A> secondList = new List<A>() { bar };
firstList.Except(secondList);
firstList.Intersect(secondList);
The output in this case is:
same entries:
>> IEnumerable<A> (1 item) 4
>> a b c
>> a 2 3,4
You can make combinations firstList.Method(secondsList) and vice versa.
You could event write a custom Comparer - IEqualityComparer in order to compare complex object types.

Categories

Resources