views:

63

answers:

3

i have the following code which doesnt seem to be working:

Context: I have two lists of objects:
* listOne has 100 records
* listTwo has 70 records

many of them have the same Id property (in both lists);

 var listOneOnlyItems = listOne.Except(listTwo, new ItemComparer ());

here is the comparer

public class ItemComparer : IEqualityComparer<Item>
{
    public bool Equals(Item x, Item y)
    {
        if (x.Id == y.Id)
            return true;

        return false;
    }

    public int GetHashCode(Item obj)
    {
        return obj.GetHashCode();
    }
}

after i run this code and look into the results

listOneOnlyItems 

still has 100 records (should only have 30). Can anyone help me?

also, running

    IEnumerable<Item> sharedItems = listOne.Intersect(listTwo, new ItemComparer());

returns zero reesults in the sharedItems collection

A: 

Consider making GetHashCode() return obj.Id.GetHashCode()

Kirk Woll
+3  A: 
public int GetHashCode(Item obj)
{
    return obj.Id.GetHashCode();
}

Worth a check at least -- IIRC GetHashCode() is tested first before equality, and if they don't have the same hash it won't bother checking equality. I'm not sure what to expect from obj.GetHashCode() -- it depends on what you've implemented on the Item class.

Peter Leppert
I confirmed this in a quick-and-dirty console app.
kbrimington
@Peter Leppert - strange, this fixed it for Except() but intersect() still shows zero items . . any thoughts?
ooo
Interesting -- reading through MSDN, it seems `Intersect` (and not `Except`) uses deferred execution -- is it possible you're seeing this? Calling `.ToList()` should force the data to be realized. Other than that, I'm stumped (aside from checking for typos). The comparer for both should be the same, and both should be called in the same way (with the deferred execution caveat)
Peter Leppert
@Peter - forget that . .it works
ooo
A: 

This code works fine:

static void TestLinqExcept()
{
    var seqA = Enumerable.Range(1, 10);
    var seqB = Enumerable.Range(1, 7);
    var seqAexceptB = seqA.Except(seqB, new IntComparer());
    foreach (var x in seqAexceptB)
    {
        Console.WriteLine(x);
    }
}

class IntComparer: EqualityComparer<int>
{
    public override bool Equals(int x, int y)
    {
        return x == y;
    }

    public override int GetHashCode(int x)
    {
        return x;
    }
}

You need to add 'override' keywords to your EqualityComparer methods. (I think not having 'override' as implicit was a mistake on the part of the C# designers).

Rafe
I beg your pardon, I just saw you were implementing IEqualityComparer. Well, that's an easy change to my test code which works fine. Note that GetHashCode *is* called every time two items are compared.
Rafe
By the way, have you considered using just seqA.Where(a => !seqB.Contains(a)), which does away with the need for the IEqualityComparer?Or, even better, use something like SortedSet.
Rafe