views:

136

answers:

1

Consider this array

        string[] presidents = {
            "Adams", "Arthur", "Buchanan", "Bush", "Carter", "Cleveland",
            "Clinton", "Coolidge", "Eisenhower", "Fillmore", "Ford", "Garfield",
            "Grant", "Harding", "Harrison", "Hayes", "Hoover", "Jackson",
            "Jefferson", "Johnson", "Kennedy", "Lincoln", "Madison", "McKinley",
            "Monroe", "Nixon", "Pierce", "Polk", "Reagan", "Roosevelt", "Taft",
            "Taylor", "Truman", "Tyler", "Van Buren", "Washington", "Wilson"};

My grouping criteria is that the name with length 1 to 5 in one group and remaining in other group.
I implemented this using this derived class

class MyLengthComparer:IEqualityComparer<Int32>
{
    public Int32 GetHashCode(Int32 i)
    {
        return i<=5?1:6;
    }


    public Boolean Equals(Int32 i1,Int32 i2)
    {
        if(i1<=5 && i2<=5)
            return true;
        if(i1>5 && i2>5)
            return true;
        else
            return false;
    }
}

Now I run this

        IEnumerable<IGrouping<Int32, String>> groupVar = presidents.GroupBy(prez=>prez.Length,new MyLengthComparer());
        foreach(IGrouping<Int32, String> grp in groupVar)
        {
            Console.WriteLine("******" + grp.Key + "******" );
            foreach(String name in grp)
                Console.WriteLine(name);
        }

        Console.ReadKey();

I want to know the meaning of the two functions of the IEqualityInterface.I mean how the comparison is actually being done.
What is the significance of Key in IGrouping, why is it showing 5 and 6?

+2  A: 

It's showing 5 and 6 because the key of the group is deemed to be the key created from the first item. "Adams" has length 5, "Arthur" has length 6, so those are the keys for the group.

The key for a group is what everything in the group is meant to have in common. Your comparer is fairly odd in this respect.

Basically, each element in the sequence is being projected to the key (the length of the name) and then there's a lookup to find out whether that key already has a group. That lookup requires the hash code (as it's a hash-based lookup) and equality - and that's what your custom comparer provides. It gives a hash of 1 or 6 depending on the length (the actual numbers are relatively unimportant; they're consistent with equality which means that the lookup will work, and they're different which means it'll be efficient). The equality function itself is really just "are these both values the same in respect to being less than or equal to 5". I would probably have implemented it as:

public Boolean Equals(Int32 i1,Int32 i2)
{
    return (i1 <= 5) == (i2 <= 5);
}

That actually makes it more obvious that you'd get the same groupings (with a different key admittedly) by using a key projection of x => x.Name.Length <= 5 and not providing a custom comparer:

var groups = presidents.GroupBy(prez => prez.Length <= 5);
foreach(var group in groups)
{
    // This time the key will be true or false
    Console.WriteLine("******" + group.Key + "******" );
    foreach(String name in group)
    {
        Console.WriteLine(name);
    }
}
Jon Skeet
Could you please provide me a step by step behaviour of the code.I mean like first Adams is scanned and it's key is found....something like that or any link to it.I just could not understand how the comparison is done by Equals and GetHashCode.
Akshay
He already gave such a nice answer ;).
Younes
@Akshay: I would separate out the LINQ part from IEqualityComparer - do you understand equality comparers to start with?
Jon Skeet
@Jon:I know IComparable and IComparer.I encountered IEqualityComparer for the first time reading about LINQ
Akshay
@Akshay: `IEqualityComparer<T>` has been around since 2.0 - it's just a generalised way of hashing and comparing objects for equality. For example, you can pass an instance to a dictionary to determine how it views key equality.
Jon Skeet