LINQ Group By w obiekcie słownika

161

Próbuję użyć LINQ, aby utworzyć Dictionary<string, List<CustomObject>>z List<CustomObject>. Mogę to uruchomić używając "var", ale nie chcę używać typów anonimowych. Oto co mam

var x = (from CustomObject o in ListOfCustomObjects
      group o by o.PropertyName into t
      select t.ToList());

Kiedyś próbowałem również użyć Cast<>()z biblioteki LINQ x, ale pojawiają się problemy z kompilacją, które powodują, że jest to nieprawidłowe rzutowanie.

Atari2600
źródło
Co jeśli spróbujesz var x = (z CustomObject o w grupie ListOfCustomObjects o przez o.PropertyName do t select t) .ToList ();
esastincy
44
Czy jest jakiś powód, dla którego musisz to zrobić, zamiast używać ToLookup, który jest przeznaczony do tego?
Jon Skeet
1
Jon, czy mógłbyś opublikować przykład, jak działa ToLookup w tej sytuacji? Nie znam tej metody LINQ.
Atari2600
8
@JonSkeet Jesteś niesamowity! (To znaczy, wszyscy już to wiedzieli, ale nadal.) Powodem, dla którego nie planowałem używać ToLookup, było to, że nigdy o tym nie słyszałem, aż do teraz. Teraz wiem!
neminem
1
Dla zachowania kompletności, używanie varnie jest typem „anonimowym”, lecz typem „niejawnym”. Typy anonimowe to nowe klasy utworzone przez kompilator do obsługi konstrukcji new { thing = "stuff" };. Typy niejawne są istniejącymi klasami, varsą po prostu wygodnym sposobem na odwołanie się do nich, gdy zmienna jest przypisywana natychmiastowo, typ zmiennej można wywnioskować z typu przypisywanego do niej obiektu. Możesz nawet niejawnie wpisać zmienną odwołującą się do typu anonimowego, np .:var a = new { thing = "stuff" };
Michael Blackburn

Odpowiedzi:

351
Dictionary<string, List<CustomObject>> myDictionary = ListOfCustomObjects
    .GroupBy(o => o.PropertyName)
    .ToDictionary(g => g.Key, g => g.ToList());
Yuriy Faktorovich
źródło
6
Jeśli nie potrzebujesz właściwości z 'CustomObject' jako wartości listy (nie pokazanej w tej odpowiedzi), warto sprawdzić jego kodowość komentarz Jona Skeeta do pytania polecającego ToLookup ().
Shaun
3
jest to sposób na zrobienie tego, jeśli pożądany jest niezmienny wynik. ToLookup jest niezmienna.
Amit
1
Moje 2 centy (tylko dlatego, że walczyłem przez godzinę :)): podczas grupowania według nieruchomości upewnij się, że właściwość MA wartość! W przeciwnym razie metoda Todict nie generuje klucza (dla właściwości String przynajmniej ...) :)
dba
.GroupBy(o => o.PropertyName).ToDictionary(g => g.Key, g => g.ToList())Może to być część rozszerzenia biblioteki Linq. więc musimy tylko zrobić.ToDictionary(o=>o.PropertyName)
Jaider
3
@Jaider istnieje już taka funkcjonalność: po prostu zastąpić ToDictionaryz ToLookup.
Robert Synoradzki
19

Nie mogę komentować @Michael Blackburn, ale myślę, że dostałeś głos przeciw, ponieważ GroupBy nie jest w tym przypadku konieczne.

Używaj go jak:

var lookupOfCustomObjects = listOfCustomObjects.ToLookup(o=>o.PropertyName);
var listWithAllCustomObjectsWithPropertyName = lookupOfCustomObjects[propertyName]

Dodatkowo widziałem, że to działa o wiele lepiej niż przy użyciu GroupBy () .ToDictionary ().

RuudvK
źródło
Wykonałem transliterację, nie odpowiadając na pytanie w najlepszy możliwy sposób.
Michael Blackburn
1

Dla @ atari2600 odpowiedź wyglądałaby tak, jak przy użyciu ToLookup w składni lambda:

var x = listOfCustomObjects
    .GroupBy(o => o.PropertyName)
    .ToLookup(customObject => customObject);

Zasadniczo pobiera IGrouping i materializuje go dla Ciebie w słowniku list, z wartościami PropertyName jako kluczem.

Michael Blackburn
źródło
Dlaczego głos przeciw? Czy to nie jest dokładne / nie odpowiada na pytanie?
Michael Blackburn
1
Na wypadek, gdybyś to przegapił, @RuudvK wspomniał w swojej odpowiedzi , że podejrzewa, że ​​głos przeciwny wynika z tego, że GroupBy jest niepotrzebne. ToLookupma przeciążenie, które wykona zadanie.
Jeff B
Brakowało mi tej odpowiedzi, dzięki za oznaczenie mnie. Ma sens, ponieważ grupowanie jest składniowo niepotrzebne, zostawiłem je tylko po to, aby przejście od składni zapytania do składni metody było jaśniejsze.
Michael Blackburn
GroupBy (przeciążenie z tylko jednym parametrem) zwraca IEnumerable <IGrouping <TKey, TSource >>, a następnie ToLookup przekształca go w dość skomplikowany typ, który nie jest nawet podobny do IDictionary <TKey, IList <TSource>> tylko ToLookup zwraca właściwy typ.
xtofs
-1

Poniższe zadziałały dla mnie.

var temp = ctx.Set<DbTable>()
  .GroupBy(g => new { g.id })
  .ToDictionary(d => d.Key.id);
Leo Barbas
źródło