2012-04-10 8 views
18

Stworzyłem słownika obiektusłownik C# Jak dodać wiele wartości dla pojedynczego klawisza?

Dictionary<string, List<string>> dictionary = 
    new Dictionary<string,List<string>>(); 

chcę dodać wartości ciągów do listy ciąg dla danego pojedynczego klawisza. Jeśli klucz już nie istnieje, muszę dodać nowy klucz. List<string> nie jest predefiniowany, tzn. Nie utworzyłem żadnego obiektu listy, a następnie dostarczono go do dictionary.Add("key",Listname). Jak utworzyć dynamicznie ten obiekt listy w dictionary.Add("key",Listname), a następnie dodaj ciągi do tej listy. Jeśli muszę dodać 100 kluczy, czy muszę utworzyć 100 list przed wykonaniem instrukcji dictionary.Add, a także czy muszę pedefine zawartość tej listy?

Dziękuję.

+0

To wstyd, że nie zawarli zmiennego wdrożenia 'Lookup'. Wiele logiki już istnieje, po prostu nie można dodawać do niej przedmiotów. –

Odpowiedz

25

Aktualizacja: sprawdzenie istnienia wykorzystaniem TryGetValue zrobić tylko jedno wyszukiwanie w przypadku, gdy masz listę:

List<int> list; 

if (!dictionary.TryGetValue("foo", out list)) 
{ 
    list = new List<int>(); 
    dictionary.Add("foo", list); 
} 

list.Add(2); 


oryginalny: Sprawdzenie istnienia i dodać jeden raz, a następnie klawisz do słownika, aby uzyskać listę i dodać ją do listy jak zwykle:

var dictionary = new Dictionary<string, List<int>>(); 

if (!dictionary.ContainsKey("foo")) 
    dictionary.Add("foo", new List<int>()); 

dictionary["foo"].Add(42); 
dictionary["foo"].AddRange(oneHundredInts); 

O r List<string> jak w twoim przypadku.

Odkładając na bok, jeśli wiesz, ile przedmiotów zamierzasz dodać do kolekcji dynamicznej, takich jak List<T>, skorzystaj z konstruktora, który ma początkową pojemność listy: new List<int>(100);.

Spowoduje to pobranie wymaganej pamięci o określonej pojemności z góry, zamiast chwytać małe porcje za każdym razem, gdy zacznie się wypełniać. Możesz zrobić to samo ze słownikami, jeśli wiesz, że masz 100 kluczy.

+0

To (zawsze) wymaga 2 wyszukiwań. –

+2

Korzystanie z TryGetValue byłoby bardziej wydajne niż ContainsKey i ponowne indeksowanie do słownika. – roken

+1

@Roken Jestem świadomy, ale to nie jest sedno problemu. Nigdy też nie widziałem żadnych wartościowych problemów związanych z wydajnością, wynikających z używania słowników w ten sposób. Przedwczesna lub mikrooptymalizacja w najlepszym przypadku. –

6

Gdybym zrozumiał, co chcesz:

dictionary.Add("key", new List<string>()); 

później ...

dictionary["key"].Add("string to your list"); 
0

Po dodaniu ciąg, robią to w różny sposób w zależności od tego, czy klucz lub już nie istnieje. Aby dodać ciąg value dla klucza key:

List<string> list; 
if (dictionary.ContainsKey(key)) { 
    list = dictionary[key]; 
} else { 
    list = new List<string>(); 
    dictionary.Add(ley, list); 
} 
list.Add(value); 
3

Można używać wdrożenie multimapy, która wywodzi się z Dictionary<K, List<V>>. Nie jest doskonały, ale wykonuje dobrą robotę.

/// <summary> 
/// Represents a collection of keys and values. 
/// Multiple values can have the same key. 
/// </summary> 
/// <typeparam name="TKey">Type of the keys.</typeparam> 
/// <typeparam name="TValue">Type of the values.</typeparam> 
public class MultiMap<TKey, TValue> : Dictionary<TKey, List<TValue>> 
{ 

    public MultiMap() 
     : base() 
    { 
    } 

    public MultiMap(int capacity) 
     : base(capacity) 
    { 
    } 

    /// <summary> 
    /// Adds an element with the specified key and value into the MultiMap. 
    /// </summary> 
    /// <param name="key">The key of the element to add.</param> 
    /// <param name="value">The value of the element to add.</param> 
    public void Add(TKey key, TValue value) 
    { 
     List<TValue> valueList; 

     if (TryGetValue(key, out valueList)) { 
      valueList.Add(value); 
     } else { 
      valueList = new List<TValue>(); 
      valueList.Add(value); 
      Add(key, valueList); 
     } 
    } 

    /// <summary> 
    /// Removes first occurence of an element with a specified key and value. 
    /// </summary> 
    /// <param name="key">The key of the element to remove.</param> 
    /// <param name="value">The value of the element to remove.</param> 
    /// <returns>true if the an element is removed; 
    /// false if the key or the value were not found.</returns> 
    public bool Remove(TKey key, TValue value) 
    { 
     List<TValue> valueList; 

     if (TryGetValue(key, out valueList)) { 
      if (valueList.Remove(value)) { 
       if (valueList.Count == 0) { 
        Remove(key); 
       } 
       return true; 
      } 
     } 
     return false; 
    } 

    /// <summary> 
    /// Removes all occurences of elements with a specified key and value. 
    /// </summary> 
    /// <param name="key">The key of the elements to remove.</param> 
    /// <param name="value">The value of the elements to remove.</param> 
    /// <returns>Number of elements removed.</returns> 
    public int RemoveAll(TKey key, TValue value) 
    { 
     List<TValue> valueList; 
     int n = 0; 

     if (TryGetValue(key, out valueList)) { 
      while (valueList.Remove(value)) { 
       n++; 
      } 
      if (valueList.Count == 0) { 
       Remove(key); 
      } 
     } 
     return n; 
    } 

    /// <summary> 
    /// Gets the total number of values contained in the MultiMap. 
    /// </summary> 
    public int CountAll 
    { 
     get 
     { 
      int n = 0; 

      foreach (List<TValue> valueList in Values) { 
       n += valueList.Count; 
      } 
      return n; 
     } 
    } 

    /// <summary> 
    /// Determines whether the MultiMap contains an element with a specific 
    /// key/value pair. 
    /// </summary> 
    /// <param name="key">Key of the element to search for.</param> 
    /// <param name="value">Value of the element to search for.</param> 
    /// <returns>true if the element was found; otherwise false.</returns> 
    public bool Contains(TKey key, TValue value) 
    { 
     List<TValue> valueList; 

     if (TryGetValue(key, out valueList)) { 
      return valueList.Contains(value); 
     } 
     return false; 
    } 

    /// <summary> 
    /// Determines whether the MultiMap contains an element with a specific value. 
    /// </summary> 
    /// <param name="value">Value of the element to search for.</param> 
    /// <returns>true if the element was found; otherwise false.</returns> 
    public bool Contains(TValue value) 
    { 
     foreach (List<TValue> valueList in Values) { 
      if (valueList.Contains(value)) { 
       return true; 
      } 
     } 
     return false; 
    } 

} 

Należy zauważyć, że metoda Add sprawdza, czy klucz jest już obecny. Jeśli klucz jest nowy, tworzona jest nowa lista, wartość jest dodawana do listy, a lista jest dodawana do słownika. Jeśli klucz był już obecny, nowa wartość zostanie dodana do istniejącej listy.

+0

Jeśli zamierzasz zabrać go na ten poziom abstrakcji, może użyć 'Dictionary >.Wykonuje się tylko kontrole add/remove/contains na wewnętrznej kolekcji, do czego jest idealny HashSet. – Servy

+1

Semantyka jest nieco inna. Moja implementacja pozwala na wielokrotne wstawianie tych samych wartości dla tego samego klucza. Nie wiem, czy istnieją różne warunki dla tych dwóch wariantów. Do którego z nich stosuje się 'MultiMap'? Może być "MultiMap" dla mojego wariantu i 'MultiSet' dla Twojego wariantu? –

+0

W ogóle nie użyłbym dziedziczenia. Użytkownicy tej klasy chcieliby całkowicie ukryć interfejs Słownika. Potrzebujesz albo MultiMapy, albo Słownika, ale nie obu. – Trap

5
Dictionary<string, List<string>> dictionary = new Dictionary<string,List<string>>(); 

foreach(string key in keys) { 
    if(!dictionary.ContainsKey(key)) { 
     //add 
     dictionary.Add(key, new List<string>()); 
    } 
    dictionary[key].Add("theString"); 
} 

Jeśli klucz nie istnieje, nowy List dodaje (jeśli wewnątrz). Inaczej ten klucz istnieje, więc po prostu dodaj nową wartość do List pod tym kluczem.

2

Chociaż prawie tak samo jak większość innych odpowiedzi, uważam, że jest to najbardziej skuteczny i zwięzły sposób na jego wdrożenie. Używanie TryGetValue jest szybsze niż użycie ContainsKey i ponowne indeksowanie do słownika, co pokazały niektóre inne rozwiązania.

void Add(string key, string val) 
{ 
    List<string> list; 

    if (!dictionary.TryGetValue(someKey, out list)) 
    { 
     values = new List<string>(); 
     dictionary.Add(key, list); 
    } 

    list.Add(val); 
} 
0

Zamiast używać słownika, dlaczego nie przekonwertować go na ILookup?

var myData = new[]{new {a=1,b="frog"}, new {a=1,b="cat"}, new {a=2,b="giraffe"}}; 
ILookup<int,string> lookup = myData.ToLookup(x => x.a, x => x.b); 
IEnumerable<string> allOnes = lookup[1]; //enumerable of 2 items, frog and cat 

ILookup to niezmienna struktura danych, która pozwala na wiele wartości na klucz. Prawdopodobnie nie jest to zbyt przydatne, jeśli musisz dodawać elementy w różnych momentach, ale jeśli masz wszystkie dane z góry, jest to zdecydowanie droga.

+0

Dziękuję. muszę dodać przedmioty w różnych momentach. – sailer

2

Użyj polecenia NameValuedCollection.

Dobry punkt wyjścia to here. Prosto z linku.

System.Collections.Specialized.NameValueCollection myCollection 
    = new System.Collections.Specialized.NameValueCollection(); 

    myCollection.Add(“Arcane”, “http://arcanecode.com”); 
    myCollection.Add(“PWOP”, “http://dotnetrocks.com”); 
    myCollection.Add(“PWOP”, “http://dnrtv.com”); 
    myCollection.Add(“PWOP”, “http://www.hanselminutes.com”); 
    myCollection.Add(“TWIT”, “http://www.twit.tv”); 
    myCollection.Add(“TWIT”, “http://www.twit.tv/SN”); 
+0

Dzięki, ta również dobra opcja – sailer

+0

1. Jest to NameValueCollection - bez "d" i 2. pamiętaj, że powinieneś użyć GetValues ​​(String) zamiast indeksu - indeksator zwraca ciąg znaków oddzielonych przecinkami z twoimi wartościami, co jest problematyczne jeśli twoje wartości mogą zawierać przecinek i 3. kolekcja nie rozróżnia wartości null jako wartości lub null jako klucza-nie-znaleziono – toong

0

Oto wiele odmian jednej odpowiedzi :) Mój jest inny i wykorzystuje mechanizm wewnętrzny jako wygodny sposób wykonać (przydatne):

public static void AddToList<T, U>(this IDictionary<T, List<U>> dict, T key, U elementToList) 
{ 

    List<U> list; 

    bool exists = dict.TryGetValue(key, out list); 

    if (exists) 
    { 
     dict[key].Add(elementToList); 
    } 
    else 
    { 
     dict[key] = new List<U>(); 
     dict[key].Add(elementToList); 
    } 

} 

Potem go używać w następujący sposób:

Dictionary<int, List<string>> dict = new Dictionary<int, List<string>>(); 

dict.AddToList(4, "test1"); 
dict.AddToList(4, "test2"); 
dict.AddToList(4, "test3"); 

dict.AddToList(5, "test4"); 
0

jest to pakiet Nuget Microsoft Experimental Collections który zawiera klasę MultiValueDictionary który robi dokładnie to, czego potrzebujesz.

Here to blog autora twórcy pakietu, który opisuje go dalej.

Here to kolejny wpis na blogu, jeśli jesteś ciekawy.

Przykład użycia:

MultiDictionary<string, int> myDictionary = new MultiDictionary<string, int>(); 
myDictionary.Add("key", 1); 
myDictionary.Add("key", 2); 
myDictionary.Add("key", 3); 
//myDictionary["key"] now contains the values 1, 2, and 3 
0

starałem się dodać do listy istniejący klucz w słowniku i osiągnął następujące rozwiązanie:

Dictionary<string,List<string>> NewParent = new Dictionary<string,List<string>>(); 
child = new List<string>(); 
child.Add('SomeData'); 
NewParent["item1"].AddRange(child); 

To nie pokaże żadnego wyjątku i nie będzie zastąp poprzednie wartości.

Powiązane problemy