Linq с groupby и count

Это довольно просто, но я в недоумении: Учитывая этот тип набора данных:

UserInfo(name, metric, day, other_metric)

и этот образец данных:

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

Я бы хотел получить таблицу, в которой перечислены показатели в порядке (0,1,2,3..) с общим количеством раз, когда счетчик встречается. Поэтому из этого набора вы получите:

0 3    
1 2    
2 2    
3 1

Я сражаюсь с синтаксисом LINQ, но я застрял на том, где положить группу и подсчитать.... любая помощь?

POST Edit: я никогда не мог заставить опубликованные ответы работать, поскольку они всегда возвращали одну запись с количеством разных счетчиков. Однако мне удалось собрать пример LINQ to SQL, который работал:

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

Этот результат дал мне упорядоченный набор записей с метрикой и количеством пользователей, связанных с ними. Я явно новичок в LINQ в целом, и для моего неподготовленного глаза этот подход кажется очень похожим на чистый подход LINQ, но дал мне другой ответ.

Ответ 1

После вызова GroupBy вы получите серию групп IEnumerable<Grouping>, где каждая группа группирует текст Key, используемый для создания группы, а также IEnumerable<T> всех элементов в исходном наборе данных. Вам просто нужно вызвать Count() в этой Группировке, чтобы получить промежуточный итог.

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);
}


Это был блестяще быстрый ответ, но у меня проблема с первой строкой, в частности "data.groupby(info = > info.metric)"

Я предполагаю, что у вас уже есть список/массив некоторых class, которые выглядят как

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

Когда вы выполняете data.GroupBy(x => x.metric), это означает "для каждого элемента x в IEnumerable, определяемом data, вычислите его .metric, затем сгруппируйте все элементы с той же метрикой в ​​Grouping и верните a IEnumerable всех результирующих групп. Учитывая ваш пример набора данных

    <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

это приведет к следующему результату после 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]

Ответ 2

Предполагая, что userInfoList является List<UserInfo>:

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

Лямбда-функция для GroupBy(), n => n.metric означает, что она получит поле metric от каждого встреченного объекта UserInfo. Тип n зависит от контекста, в первом вхождении он имеет тип UserInfo, потому что в списке содержатся объекты UserInfo. Во втором вхождении n имеет тип Grouping, потому что теперь он представляет собой список объектов Grouping.

Grouping имеют методы расширения, такие как .Count(), .Key() и почти все, что вы ожидаете. Так же, как вы проверили бы .Lenght на string, вы можете проверить .Count() на группе.

Ответ 3

userInfos.GroupBy(userInfo => userInfo.metric)
        .OrderBy(group => group.Key)
        .Select(group => Tuple.Create(group.key, group.Count()));