Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using strict types in linq grouping query

Tags:

c#

linq

I'm querying a collection using linq and grouping my data by datetime property using the following code:

var querty = from post in ds.Appointments
             group post by new  
             { 
                 Year = post.DateOfVisit.Year, 
                 Month = post.DateOfVisit.Month
             };

Everything is good, when use an anonymous type. But If I define my own class

class YearMonth
{
    public int Year;
    public string Month;

    public YearMonth(int year, int month)
    {
        Year = year;
        Month = month;
    }

    public override string ToString()
    {
        return string.Format("{0}-{1}",Year,Month);
    }
}

and modify my query accordingly

var querty = from post in ds.Appointments
             group post by new YearMonth(post.DateOfVisit.Year, 
                                         post.DateOfVisit.Month);

then grouping is not working and I'm getting a plain list of the objects. Why?

like image 265
TOP KEK Avatar asked Mar 28 '14 11:03

TOP KEK


1 Answers

As schglurps already said, you have to override GetHashCode and Equals, since the GroupBy method (and others) rely on these.


The GroupBy method creates the final groups based on the hash code (and equality) of the objects it groups on.

So for each item in your sequence, it checks if there's already a group with the same hash code, then checks if the item is equal to the key of the group. If there's no matching group, it creates a new one.

In your case, since you don't override GetHashCode, each instance of YearMonth will have a different hash code (collisions aside), so each item will result in a new group being created.

Just have a look at the relevant reference source.


Here's an example implementation:

public class YearMonth : IEquatable<YearMonth>
{
    public readonly int Year;
    public readonly int Month;

    public YearMonth(int year, int month)
    {
        Year = year;
        Month = month;
    }

    public override string ToString()
    {
        return string.Format("{0}-{1}", Year, Month);
    }

    public bool Equals(YearMonth other)
    {
        if (ReferenceEquals(null, other)) return false;
        if (ReferenceEquals(this, other)) return true;
        return Month == other.Month && Year == other.Year;
    }

    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((YearMonth)obj);
    }

    public override int GetHashCode()
    {
        unchecked
        {
            return (Month * 397) ^ Year;
        }
    }

    public static bool operator ==(YearMonth left, YearMonth right)
    {
        return Equals(left, right);
    }

    public static bool operator !=(YearMonth left, YearMonth right)
    {
        return !Equals(left, right);
    }
}
like image 121
sloth Avatar answered Oct 12 '22 14:10

sloth