2012-04-10 4 views
18

Я создал объект словаряC# dictionary Как добавить несколько значений для одного ключа?

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

Я хочу, чтобы добавить строковые значения в списке строки для данного одного ключа. Если ключ еще не существует, я должен добавить новый ключ. List<string> не является предопределенным, я имею в виду, что я не создал объект списка, а затем был отправлен в dictionary.Add("key",Listname). Как создать динамически этот объект списка в dictionary.Add("key",Listname), а затем добавить строки в этот список. Если мне нужно добавить 100 ключей, тогда мне нужно создать 100 списков, прежде чем выполнить инструкцию dictionary.Add, а также мне нужно будет проиндексировать содержимое этих списков?

спасибо.

+0

Жаль, что они не включили изменчивую реализацию 'Lookup'. Много логики уже есть, вы просто не можете добавлять в нее элементы. –

ответ

25

Обновление: проверка на существование с помощью TryGetValue сделать только один поиск в том случае, если у вас есть список:

List<int> list; 

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

list.Add(2); 


Оригинал: Проверка существования и добавить один раз, а затем клавишу в словарь, чтобы получить список и добавить в список, как обычно:

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

0 r List<string> как в вашем случае.

Как известно, если вы знаете, сколько элементов вы собираетесь добавить в динамическую коллекцию, например List<T>, воспользуйтесь конструктором, который берет начальную емкость списка: new List<int>(100);.

Это займет память, необходимую для удовлетворения заданной емкости upfront, вместо того, чтобы захватывать небольшие куски каждый раз, когда она начинает заполняться. Вы можете сделать то же самое со словарями, если знаете, что у вас 100 ключей.

+0

Это (всегда) занимает 2 поисковых запросов. –

+2

Использование TryGetValue было бы более эффективным, чем ContainsKey и переиндексирование в словарь. – roken

+1

@Roken Я знаю, но это не суть вопроса. Я также не видел каких-либо стоящих проблем с производительностью, основанных на использовании словарей таким образом. Преждевременная или микро-оптимизация в лучшем случае. –

6

Если я понял, что вы хотите:

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

позже ...

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

При добавлении строки, сделать это по-разному в зависимости от того, уже или нет, существует ключ. Чтобы добавить строку value для ключа key:

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

Вы можете использовать мою реализацию Multimap, которая вытекает из Dictionary<K, List<V>>. Это не идеально, но это хорошо.

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

} 

Обратите внимание, что метод Add выглядит, если ключ уже присутствует. Если ключ новый, создается новый список, значение добавляется в список, а список добавляется в словарь. Если ключ уже присутствовал, новое значение добавляется в существующий список.

+0

Если вы перейдете на этот уровень абстракции, почему бы не использовать словарь >.Вы только выполняете команды add/remove/contains во внутренней коллекции, что и идеально подходит для HashSet. – Servy

+1

Семантика немного отличается. Моя реализация позволяет вставлять одни и те же значения несколько раз для одного и того же ключа. Я не знаю, существуют ли разные термины для этих двух вариантов. Для чего применяется «MultiMap»? Может быть «MultiMap» для моего варианта и «MultiSet» для вашего варианта? –

+0

Я бы не использовал наследование вообще. Пользователи этого класса хотели бы, чтобы интерфейс Dictionary полностью скрывался. Вы либо хотите MultiMap, либо словарь, но не оба. – 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"); 
} 

Если ключ не существует, новый List добавляется (в случае). Иначе ключ существует, поэтому просто добавьте новое значение к List под этим ключом.

2

Хотя это почти то же самое, что и большинство других ответов, я считаю, что это самый эффективный и лаконичный способ его реализации. Использование TryGetValue происходит быстрее, чем использование ContainsKey и переиндексация в словарь, как показали некоторые другие решения.

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

Вместо использования словаря, почему бы не преобразовать его в 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 - это неизменяемая структура данных, которая допускает множество значений для каждого ключа. Вероятно, не так много пользы, если вам нужно добавлять элементы в разное время, но если у вас есть все ваши данные вверх, это определенно путь.

+0

Спасибо. Мне нужно добавлять элементы в разное время. – sailer

2

Использование NameValuedCollection.

Хорошая отправная точка: here. Прямо от ссылки.

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

Спасибо, это тоже хорошая опция – sailer

+0

1. Это имяValueCollection - без 'd' и 2. обратите внимание, что вместо индексатора следует использовать GetValues ​​(String) - индексщик возвращает строку, разделенную запятой, с вашими значениями, что является проблематичным если ваши значения могут содержать запятую и 3. коллекция не различает нуль как значение или значение null как не найденное ключом – toong

0

Здесь множество вариаций одного ответа :) Мой еще один, и он использует механизм расширения, как удобный способ для выполнения (удобно):

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

} 

Затем вы используете его следующим образом:

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

Существует пакет Microsoft Experimental Collections NuGet, который содержит класс MultiValueDictionary, который делает именно то, что вам нужно.

Here - это сообщение в блоге создателя пакета, который описывает его дальше.

Here - еще одно сообщение в блоге, если вам интересно.

Пример:

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

Я пытался добавить список в существующий ключ в словаре и достиг следующее решение:

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

Он не будет показывать никаких исключений и не будет замените предыдущие значения.

Смежные вопросы