2012-12-19 12 views
41

public class Person 
{ 
    public string NickName{ get; set; } 
    public string Name{ get; set; } 
} 

var pl = new List<Person>; 

var q = from p in pl 
     where p.Name.First() == 'A' 
     orderby p.NickName 
     select new KeyValuePair<String, String>(p.NickName, p.Name); 

var d1 = q.ToList(); // Gives List<KeyValuePair<string, string>> 
var d2 = q.ToDictionary(); // Does not compile 

Jak uzyskać słownik < string, string >?Jak napisać zapytanie LINQ powodujące słownik?

+3

jak o 'new Dictionary (d1);' lub 'd1 = q.ToDictionary (p => p.Key, p => p.Value);' – Jodrell

+0

@Jodrell powodzenia kompilowanie tego 'nowego' oświadczenia – Alex

+1

@Alex, masz rację, nie ma' IEnumerable > 'Przeciążenie contructor. – Jodrell

Odpowiedz

40

musisz określić wartości dla Dictionary

var d2 = q.ToDictionary(p => p.NickName, p => p.Name); 
5

spróbuj wykonać za nick jako klucz i nazwę jako wartość

var d2 = q.ToDictionary (p => p.NickName, p=>p.Name); 

jednak pamiętać, że słownik nie pozwala na duplikat, więc powyżej woli throw error dla duplikatów rekordów z tym samym pseudonimem. Może chcesz użyć Lookup, który jest podobny do słownika, ale pozwala powiela

var d2 = q.ToLookup (p => p.NickName, p=>p.Name); 
+0

Określanie, że klucz KeyValuePair jest kluczem, a jego wartość to wartość podobna do kodu Captain Obvious (powinno to być naprawdę niejawne, IMHO), ale wydaje się, że rozwiązuje problem, dziękuję. – Ivan

+0

co niejawne nie będzie działać dla wszystkich typów sekwencji. Będzie działać tylko dla sekwencji powyżej KeyValuePair, ale wtedy będziesz potrzebował kolejnej warstwy pomiędzy, aby wygenerować keyvaluepair. – Tilak

+0

@Ivan, ale tak naprawdę nie ma potrzeby tworzenia pośredniej "Listy >", wystarczy wyraźna składnia na końcu zapytania. Jak w mojej odpowiedzi. Możesz także stracić zamówienie. – Jodrell

8

EDIT

Jeśli naprawdę czujesz, że trzeba się z IEnumerable<KeyValuePair<TKey, TValue>> do Dictionary domyśle można dodać to rozszerzenie.

public static IDictionary<TKey, ToValue> ToDictionary<TKey, TValue>(
    this IEnumerable<KeyValuePair<TKey, TValue>> source) 
{ 
    return source.ToDictionary(p => p.Key, p => p.Value); 
} 

Następnie można nazwać ToDictionary() na dowolnym IEnumerable<KeyValuePair<TKey, TValue>>.

EDIT 2

Jeśli przewidywania duplikaty a następnie można utworzyć ToLookup() rozszerzenie zbyt.

public static ILookup<TKey, TValue> ToLookup<TKey, TValue>(
    this IEnumerable<KeyValuePair<TKey, TValue>> source) 
{ 
    return source.ToLookup(p => p.Key, p => p.Value); 
} 

Alternatywnie, jeśli naprawdę chcesz, aby odrzucić wyniki, można dodać przeciążenie dla ToDictionary.

public static IDictionary<TKey, ToValue> ToDictionary<TKey, TValue>(
    this IEnumerable<KeyValuePair<TKey, TValue>> source, 
    Func<<IEnumerable<TValue>, TValue> selector) 
{ 
    return source 
     .Lookup(p => p.Key, p => p.Value); 
     .ToDictionary(l => l.Key, l => selector(l)); 
} 

Jeśli arbitralnie odrzucić wszystko, ale „pierwsze” (co to znaczy bez OrderBy) elementu, można użyć tego rozszerzenia w ten sposób,

pairs.ToDictionary(v => v.First()); 

Ogólnie rzecz biorąc, można usunąć większość kodu i zrobić,

var q = from p in pl 
     where p.Name.First() == 'A'; 
var d = q.ToDictionary(p => p.NickName, p => p.Name); 

Gdybym mógł tam być duplikatów, do

var d = q.ToLookup(p => p.NickName, p => p.Name); 

ale uwaga, to zwraca ILookup<TKey, TElement> The Item podziałowe z których zwraca IEnumerable<TElement> więc nie odrzucić dane.

+0

+1 dla metody rozszerzenia - dokładnie to robię – sq33G

12

Słownik nie może zawierać wielu równych kluczy, więc należy upewnić się (lub wiedzieć), że tak nie jest.Można użyć GroupBy aby zapewnić go:

Dictionary<string, string> dict = pl 
     .Where(p => p.Name.First() == 'A') 
     .GroupBy(p => p.NickName) 
     .ToDictionary(g => g.Key, g => g.First().Name); 
+0

Dlaczego nie 'ToLookup()'? Czy należy zamówić przez "Pseudonim" w podzapytanie, gdy już pogrupowane przez 'Pseudonim'? – Jodrell

+0

@Jodrell: Dlaczego 'ToLookup'? OP prosi o wyraźny słownik. Tak, 'OrderBy' jest zbędny (jak w kodzie OP). Usuwam to. –

+0

Ponieważ nie mogę myśleć o sytuacji, w której odrzucanie danych ze względu na sposób, w jaki jest to wyliczone, ma sens. 'OrderBy' na' IGrouping' może sprawić, że będzie mniej arbriterem. – Jodrell

0

zdaję sobie sprawę, że ten został oznaczony , ale byłem dosłownie tylko próbuje dowiedzieć się, jak to zrobić w wczoraj, więc pomyślałem, że podzielę jak chcesz to zrobić w VB, a także:

Public Class Person 
    Property NickName As String 
    Property Name As String 
End Class 

Sub Main() 
    Dim p1 As New List(Of Person) 

    '*** Fill the list here *** 

    Dim q = (From p In p1 
      Where p.Name.First = "A" 
      Select p.NickName, p.Name).ToDictionary(
              Function(k) k.NickName, 
              Function(v) v.Name) 
End Sub 
+0

Ale nie potrzebujesz również pośredniej 'KeyValuePair' lub' OrderBy' w VB. – Jodrell

+0

To prawda. Po prostu wziąłem kod z pytania, przekonwertowałem go na VB, a następnie dodałem część "ToDictionary". :) – bhamby

Powiązane problemy