1

I have an IEqualityComparer in c#:

public class ScheduledTimeComparer : IEqualityComparer<ScheduledTime>
{
    public bool Equals(ScheduledTime x, ScheduledTime y)
    {
        if (x == y) return true;
        if (x == null) return false;
        return GetHashCode(x) == GetHashCode(y);
    }

    public int GetHashCode(ScheduledTime schedule)
    {
        return schedule.Start.GetHashCode() ^ schedule.End.GetHashCode();
    }
}

public class ScheduledTime
{
    public int Start { get; set; }
    public int End { get; set; }
}

I have the following 2 list of ScheduledTimes objects:

List1 = [{ Start = 60, End = 120 }]

List2 = [{ Start = 180, End = 240 }, { Start = 60, End = 120 }]

Now when i use the above mentioned equality comparer like:

//Count should be greater than 0 because both List1 and List2 are not equal
var equal = List1.Except(List2, new ScheduledTimeComparer()).Count == 0;

The Count is always zero, What i am doing wrong ?

user1740381
  • 2,121
  • 8
  • 37
  • 61
  • Your Equals method will return true for {Start=1, End=2} and {Start=2, End=1} because it xors the two hash codes together. So the result will be the same whichever order you use. – eoldre Apr 03 '14 at 18:54

4 Answers4

2

Your comparison works fine, the problem is that you're misusing Except

var equal = List1.Except(List2, new ScheduledTimeComparer()).Count == 0;

This is giving you items in list1 that aren't in list2. List 1 is a subset of list2 so you get nothing back. If you change it to;

var dif = List2.Except(List1, new ScheduledTimeComparer()).ToList();
dif.AddRange(List1.Except(List2, new ScheduleTimeComparer());
if (dif.Count() > 0)
    // the lists are different

Then you will get back 1 item and it will work as you expected.

evanmcdonnal
  • 46,131
  • 16
  • 104
  • 115
  • If the "extra" item can be in either list, this will not work well: you'd have to have both of the above lines. – Tim S. Apr 03 '14 at 19:01
  • @TimS. I wouldn't go that far... The user didn't state exactly what he was looking for. If I want the symmetric difference then I would do `List dif = list1.except(list2).ToList(); dif.AddRange(list2.Except(list1));` and that works perfectly well. Certainly not deserving of your down vote buddy, I explained his perceived bug which was really what the OP was looking for. – evanmcdonnal Apr 03 '14 at 20:53
  • I didn't downvote, someone else did. I agree that the user wasn't explicit, so I just thought of that as a potential weakness in your answer, not a "this makes the answer useless" sort of thing. – Tim S. Apr 03 '14 at 21:51
  • @TimS. oh, yeah that is a fair point, I should have been less defensive. My answer would actually introduce another bug and since the OP didn't understand `Except` in the one instance my suggestion would probably just leave him thinking it's working without knowing any better... – evanmcdonnal Apr 03 '14 at 21:58
2

List1.Except(List2... looks for items that are in List1 but not in List2. It sounds like you want to know if the lists are equal to each other. Here is one way to do that, using SetEquals:

var set1 = new HashSet<ScheduledTime>(List1, new ScheduledTimeComparer());
bool equal = set1.SetEquals(List2);

Also, your Equals and GetHashCode implementations are broken. In Equals, instead of just comparing hash codes, you need to compare values to get accurate answers. And using XOR (^) in GetHashCode is weak, e.g. anything with Start == End will hash to 0 and both of your examples hash to the same value, 68. A better option is to use multiplying by primes and addition to work out the hash codes. Also, since int.GetHashCode() returns the int itself, I omitted it. This will work better:

public class ScheduledTimeComparer : IEqualityComparer<ScheduledTime>
{
    public bool Equals(ScheduledTime x, ScheduledTime y)
    {
        if (x == y) return true;
        if (x == null) return false;
        return x.Start == y.Start && x.End == y.End;
    }

    public int GetHashCode(ScheduledTime schedule)
    {
        unchecked
        {
            return schedule.Start * 31 + schedule.End;
        }
    }
}
Community
  • 1
  • 1
Tim S.
  • 55,448
  • 7
  • 96
  • 122
1

The count should be zero since Except is set difference and there is an element in List2 equal to the single element in List1. The output sequence contains the elements of List1 not in List2, and there are no such elements using your comparer.

Lee
  • 142,018
  • 20
  • 234
  • 287
0
List1.Except(List2, new ScheduledTimeComparer()) 

means List1 \ List2 in group terms; that is, take all the elements in List1 not preset in List2. There are no such elements, so the returned result is empty.

Perhaps what you wanted to do is:

List2.Except(List1, new ScheduledTimeComparer())

Which would return a single result.

Gilthans
  • 1,656
  • 1
  • 18
  • 23