Pop
Pop

Reputation: 525

LINQ group by sum not working as expected

I have this class:

public class tempClass
{
    public int myKey { get; set; }
    public int total { get; set; }
}

Code to group by and sum:

var list = new List<tempClass>();
list.Add(new tempClass { myKey = 1, total = 1 });
list.Add(new tempClass { myKey = 1, total = 2 });
list.Add(new tempClass { myKey = 2, total = 3 });
list.Add(new tempClass { myKey = 2, total = 4 });
list = list
    .Select(w => new tempClass { myKey = w.myKey, total = w.total })
    .GroupBy(x => new tempClass { myKey = x.myKey })
    .Select(y => new tempClass { myKey = y.Key.myKey, total = y.Sum(z => z.total) })
    .ToList();

The list count is still 4 after the GroupBy.

Same result for code below:

list = list
    .GroupBy(x => new tempClass { myKey = x.myKey })
    .Select(y => new tempClass { myKey = y.Key.myKey, total = y.Sum(z => z.total) })
    .ToList();

Upvotes: 2

Views: 2728

Answers (3)

Harald Coppoolse
Harald Coppoolse

Reputation: 30492

My, you are creating a lot of new TempClass objects in your LINQ statement, don't you?

The reason that you don't get the correct result is that your GroupBy doesn't make groups of TempClass objects with the equal TempClass.MyKey, but with equal TempClass.

The default EqualityComparer for TempClass declares two TempClass objects equal if they are the same object, thus making two TempClass objects unequal, even if they have the same values.

Your query should be:

var result = list
    .GroupBy(listItem => listItem.MyKey) // make groups with equal MyKey
    .Select(group => new                 // from every group make one new item
    {
         Key = group.Key,   // with key the common MyKey in the group
         GrandTotal = group.Sum(groupItem => groupItem.Total);
                            // and value the sum of all Total values in the group
    });

I chose not to make the final resulting items a sequence of TempClasses, because I'm not sure if you would consider items with this GrandTotal as TempClass objects. But if you want, you could change the final select:

 .Select(group => new TempKey()
 {
     Key = group.Key,
     Total = group.Sum(groupItem => groupItem.Total);
 });

Upvotes: 1

Racil Hilan
Racil Hilan

Reputation: 25361

You don't need two Select lines, one is enough. And inside GroupBy, just select your key, don't create a new object of your class there:

list = list
       .GroupBy(x => x.myKey)
       .Select(y => new tempClass { myKey = y.Key, total = y.Sum(z => z.total) })
       .ToList();

And here's the declarative-query-syntax version:

list = (from x in list
        group x by x.myKey into g
        select new tempClass { myKey = g.Key, total = g.Sum(z => z.total) }).ToList();

Upvotes: 2

Tim Schmelter
Tim Schmelter

Reputation: 460238

The reason for this is that you group by a class which doesn't override Equals and GetHashCode. Then the implementation of System.Object is used which just compares references. Since all are different references you get one group for every instance.

You could group by this property or override Equals and GetHashCode to compare this property:

list = list
    .Select(w => new tempClass { myKey = w.myKey, total = w.total })
    .GroupBy(x => x.myKey)
    .Select(y => new tempClass { myKey = y.Key, total = y.Sum(z => z.total) })
    .ToList();

Upvotes: 4

Related Questions