303

This is pretty simple but I'm at a loss: Given this type of data set:

UserInfo(name, metric, day, other_metric)

and this sample data set:

joe  1 01/01/2011 5
jane 0 01/02/2011 9
john 2 01/03/2011 0
jim  3 01/04/2011 1
jean 1 01/05/2011 3
jill 2 01/06/2011 5
jeb  0 01/07/2011 3
jenn 0 01/08/2011 7

I'd like to retrieve a table that lists metrics in order(0,1,2,3..) with the total number of times the count occurs. So from this set, you'd end up with:

0 3    
1 2    
2 2    
3 1

I'm grappling with the LINQ syntax but am stuck on where to put a groupby and count... any help?

POST Edit: I was never able to get the posted answers to work as they always returned one record with the number of different counts. However, I was able to put together a LINQ to SQL example that did work:

var pl = from r in info
         orderby r.metric    
         group r by r.metric into grp
         select new { key = grp.Key, cnt = grp.Count()};

This result gave me an ordered set of records with 'metrics' and the number of users associated with each. I'm clearly new to LINQ in general and to my untrained eye this approach seems very similar to the pure LINQ approach yet gave me a different answer.

Grigory Zhadko
  • 1,484
  • 1
  • 19
  • 33
Gio
  • 4,099
  • 3
  • 30
  • 32
  • Yes I have, but jimmy's explanation helped me more so. However I was never able to get his example to work but it did lead me in a new direction. – Gio Sep 02 '11 at 19:41
  • @Jimmy used the functional syntax for LINQ expressions rather than standard LINQ query syntax, plus he decided to show the immediate execution of those functions rather than the delayed execution format. For a new person that would be confusing. Don't know why he did that. – Richard Robertson Feb 14 '19 at 15:21

3 Answers3

521

After calling GroupBy, you get a series of groups IEnumerable<Grouping>, where each Grouping itself exposes the Key used to create the group and also is an IEnumerable<T> of whatever items are in your original data set. You just have to call Count() on that Grouping to get the subtotal.

foreach(var line in data.GroupBy(info => info.metric)
                        .Select(group => new { 
                             Metric = group.Key, 
                             Count = group.Count() 
                        })
                        .OrderBy(x => x.Metric))
{
     Console.WriteLine("{0} {1}", line.Metric, line.Count);
}

> This was a brilliantly quick reply but I'm having a bit of an issue with the first line, specifically "data.groupby(info=>info.metric)"

I'm assuming you already have a list/array of some class that looks like

class UserInfo {
    string name;
    int metric;
    ..etc..
} 
...
List<UserInfo> data = ..... ;

When you do data.GroupBy(x => x.metric), it means "for each element x in the IEnumerable defined by data, calculate it's .metric, then group all the elements with the same metric into a Grouping and return an IEnumerable of all the resulting groups. Given your example data set of

    <DATA>           | Grouping Key (x=>x.metric) |
joe  1 01/01/2011 5  | 1
jane 0 01/02/2011 9  | 0
john 2 01/03/2011 0  | 2
jim  3 01/04/2011 1  | 3
jean 1 01/05/2011 3  | 1
jill 2 01/06/2011 5  | 2
jeb  0 01/07/2011 3  | 0
jenn 0 01/08/2011 7  | 0

it would result in the following result after the groupby:

(Group 1): [joe  1 01/01/2011 5, jean 1 01/05/2011 3]
(Group 0): [jane 0 01/02/2011 9, jeb  0 01/07/2011 3, jenn 0 01/08/2011 7]
(Group 2): [john 2 01/03/2011 0, jill 2 01/06/2011 5]
(Group 3): [jim  3 01/04/2011 1]
Nick Kovalsky
  • 5,378
  • 2
  • 23
  • 50
Jimmy
  • 89,068
  • 17
  • 119
  • 137
  • This was a brilliantly quick reply but I'm having a bit of an issue with the first line, specifically "data.groupby(info=>info.metric)". Plainly 'data' is the currently data set but what does 'info.metric' represet? the class definition? – Gio Sep 02 '11 at 15:49
  • "info.metric" would be the metric property/field on the UserInfo class you mentioned in your question. – lee-m Sep 02 '11 at 15:57
  • 1
    Thanks figured that out but actually this appears to give me a single value - namely the total number of different metric counts. In this example I get "metrics 4" which indicates to me how many different counts I have. – Gio Sep 02 '11 at 16:04
  • 1
    Wow. You totally explained grouping!! That alone was worth the post.... i stil get the "metrics 4" result but thanks! – Gio Sep 02 '11 at 16:09
  • Finally got it to work. Outstanding time to answer sub 1 minute. – Gio Sep 02 '11 at 20:13
  • 6
    The start of this answer, "after calling GroupBy, you get a series of groups IEnumerable, where each Grouping itself exposes the Key used to create the group and also is an IEnumerable of whatever items are in your original data set", is the clearest explanation of LINQ GroupBy I've yet read, thank you. – dumbledad Oct 15 '14 at 07:13
86

Assuming userInfoList is a List<UserInfo>:

var groups = userInfoList.GroupBy(n => n.metric)
                         .Select(n => new
                          {
                               MetricName = n.Key,
                               MetricCount = n.Count()
                          })
                         .OrderBy(n => n.MetricName);

The lambda function for GroupBy(), n => n.metric means that it will get field metric from every UserInfo object encountered. The type of n is depending on the context, in the first occurrence its of type UserInfo, because the list contains UserInfo objects. In the second occurrence n is of type Grouping, because now its a list of Grouping objects.

Groupings have extension methods like .Count(), .Key() and pretty much anything else you would expect. Just as you would check .Length on a string, you can check .Count() on a group.

haraman
  • 2,744
  • 2
  • 27
  • 50
Vladislav Zorov
  • 2,998
  • 19
  • 32
26
userInfos.GroupBy(userInfo => userInfo.metric)
        .OrderBy(group => group.Key)
        .Select(group => Tuple.Create(group.Key, group.Count()));
RoadRunner
  • 25,803
  • 6
  • 42
  • 75
DanielOfTaebl
  • 713
  • 5
  • 13