LINQ z grupowaniem i liczeniem

221

Jest to dość proste, ale jestem zagubiony: Biorąc pod uwagę ten typ zestawu danych:

UserInfo(name, metric, day, other_metric)

i ten przykładowy zestaw danych:

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

Chciałbym pobrać tabelę, która zawiera listę metryk w kolejności (0,1,2,3 ..) z całkowitą liczbą wystąpień. Więc z tego zestawu skończyłbyś:

0 3    
1 2    
2 2    
3 1

Zmagam się ze składnią LINQ, ale utknąłem na tym, gdzie umieścić groupby i liczyć .... jakiejkolwiek pomocy?

Edycja POST: Nigdy nie udało mi się sprawić, by opublikowane odpowiedzi zadziałały, ponieważ zawsze zwracały jeden rekord z liczbą różnych wyników. Udało mi się jednak stworzyć przykład LINQ-SQL, który działał:

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

Ten wynik dał mi uporządkowany zestaw rekordów z „metrykami” i liczbą powiązanych z nimi użytkowników. Jestem wyraźnie nowy w LINQ i moim niedoświadczonym okiem to podejście wydaje się bardzo podobne do czystego podejścia LINQ, ale dało mi inną odpowiedź.

Gio
źródło
Tak, ale wyjaśnienia Jimmy'ego pomogły mi bardziej. Jednak nigdy nie udało mi się nakłonić jego przykładu do działania, ale doprowadziło mnie to w nowym kierunku.
Gio
@Jimmy użył składni funkcjonalnej dla wyrażeń LINQ zamiast standardowej składni zapytań LINQ, a ponadto postanowił pokazać natychmiastowe wykonanie tych funkcji zamiast formatu opóźnionego wykonania. Dla nowej osoby byłoby to mylące. Nie wiem, dlaczego to zrobił.
Richard Robertson

Odpowiedzi:

394

Po wywołaniu GroupByotrzymujesz serię grup IEnumerable<Grouping>, w których każde z nich odsłania Keyużywane do utworzenia grupy, a także zawiera IEnumerable<T>dowolne elementy z oryginalnego zestawu danych. Musisz tylko zadzwonić Count()do tego ugrupowania, aby uzyskać sumę częściową.

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


To była bardzo szybka odpowiedź, ale mam trochę problemu z pierwszą linią, a konkretnie „data.groupby (info => info.metric)”

Zakładam, że masz już listę / tablicę niektórych, classktóre wyglądają

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

Kiedy to zrobisz data.GroupBy(x => x.metric), oznacza to „dla każdego elementu xw IEnumerable zdefiniowanym przez data, oblicz go .metric, a następnie zgrupuj wszystkie elementy o tej samej metryki w a Groupingi zwróć an IEnumerablewszystkich grup wynikowych. Biorąc pod uwagę przykładowy zestaw danych

    <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

spowoduje to następujący wynik po grupowaniu:

(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]
Jimmy
źródło
To była bardzo szybka odpowiedź, ale mam trochę problemu z pierwszą linią, a konkretnie „data.groupby (info => info.metric)”. Zwykle „dane” to aktualnie zestaw danych, ale co oznacza „info.metric”? definicja klasy?
Gio
„info.metric” to właściwość / pole metryki w klasie UserInfo, o której wspomniałeś w swoim pytaniu.
lee-m
1
Dzięki doszedłem do tego, ale tak naprawdę wydaje mi się, że daje mi to pojedynczą wartość - mianowicie całkowitą liczbę różnych liczb metryk. W tym przykładzie otrzymuję „metryki 4”, które wskazują mi, ile różnych liczb mam.
Gio
1
Łał. Całkowicie wyjaśniłeś grupowanie !! Samo było warte wpisu ... i tak otrzymuję wynik "metrics 4", ale dzięki!
Gio,
4
Początek tej odpowiedzi: „po wywołaniu GroupBy, otrzymujesz serię IEnumerable <Grouping>, gdzie każda grupa sama eksponuje klucz użyty do utworzenia grupy, a także IEnumerable <T> dowolnych elementów w oryginalnych danych set ”, to najjaśniejsze wyjaśnienie grupy LINQ. Przez to, co przeczytałem, dziękuję.
dumbledad
48

Zakładając, userInfoListże List<UserInfo>:

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

Funkcja lambda dla GroupBy(), n => n.metricoznacza to, że będzie się pole metricz każdego UserInfoprzedmiotu spotkaliśmy. Typ nzależy od kontekstu, przy pierwszym wystąpieniu jest typem UserInfo, ponieważ lista zawiera UserInfoobiekty. W drugim wystąpieniu njest typu Grouping, ponieważ teraz jest to lista Groupingobiektów.

Groupings mają metody rozszerzenie takich jak .Count(), .Key()i prawie nic innego można się spodziewać. Tak samo jak sprawdzić .Lenghtna zasadzie string, można sprawdzić .Count()na grupie.

Vladislav Zorov
źródło
23
userInfos.GroupBy(userInfo => userInfo.metric)
        .OrderBy(group => group.Key)
        .Select(group => Tuple.Create(group.Key, group.Count()));
DanielOfTaebl
źródło
1
mała literówka -> group.keyw Select (...) musi byćgroup.Key
Jan