35

I have created dictionary object

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

I want to add string values to the list of string for a given single key. If the key doesn't already exists then I have to add a new key. List<string> is not predefined, I mean I didn't create any list object and then supplied to dictionary.Add("key",Listname). How to create dynamically this list object in dictionary.Add("key",Listname) and then add strings to this list. If I have to add 100 keys then do I have to create 100 lists before executing dictionary.Add instruction and also do I have to pedefine the contents of this lists ?

Thank you.

Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188
sailer
  • 449
  • 2
  • 7
  • 12
  • 1
    It's a shame they haven't included a mutable `Lookup` implementation. A lot of the logic is there already, you just can't add items to it. – Jeff Mercado Apr 10 '12 at 13:46

13 Answers13

42

Update: check for existence using TryGetValue to do only one lookup in the case where you have the list:

List<int> list;

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

list.Add(2);


Original: Check for existence and add once, then key into the dictionary to get the list and add to the list as normal:
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);

Or List<string> as in your case.

As an aside, if you know how many items you are going to add to a dynamic collection such as List<T>, favour the constructor that takes the initial list capacity: new List<int>(100);.

This will grab the memory required to satisfy the specified capacity upfront, instead of grabbing small chunks every time it starts to fill up. You can do the same with dictionaries if you know you have 100 keys.

Adam Houldsworth
  • 63,413
  • 11
  • 150
  • 187
  • This (always) takes 2 lookups. – H H Apr 10 '12 at 13:47
  • 2
    Using TryGetValue would be more performant than ContainsKey and reindexing into the dictionary. – roken Apr 10 '12 at 13:48
  • 1
    @Roken I'm aware, but that isn't the crux of the question. Nor have I ever seen any worthwhile performance issues derived from using dictionaries in this manner. Premature or micro-optimisation at best. – Adam Houldsworth Apr 10 '12 at 13:49
  • 1
    @AdamHouldsworth We benchmark our code at the nanosecond level. Just because you don't see the value in the most efficient solution doesn't mean it isn't valuable to others who work in a different domain than you do. – roken Apr 10 '12 at 13:56
  • 4
    @Roken Then I'd fail to see why you are using C#, .NET and a CLR you cannot control personally. In this language, I don't sweat the odd fraction of a second; and don't misconstrue my answer as not seeing the value - I just value other stuff a lot more than that. – Adam Houldsworth Apr 10 '12 at 13:58
10

If I understood what you want:

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

later...

dictionary["key"].Add("string to your list");
Renatas M.
  • 11,694
  • 1
  • 43
  • 62
7
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");
}

If the key doesn't exist, a new List is added (inside if). Else the key exists, so just add a new value to the List under that key.

JBert
  • 3,311
  • 24
  • 37
Zelter Ady
  • 6,266
  • 12
  • 48
  • 75
4

You could use my implementation of a multimap, which derives from a Dictionary<K, List<V>>. It is not perfect, however it does a good job.

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

}

Note that the Add method looks if a key is already present. If the key is new, a new list is created, the value is added to the list and the list is added to the dictionary. If the key was already present, the new value is added to the existing list.

Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188
  • If you're going to take it to this level of abstraction why not use a `Dictionary>`. You only perform add/remove/contains checks on the inner collection, which is what a HashSet is ideal for. – Servy Apr 10 '12 at 13:42
  • 1
    The semantics is slightly different. My implementation allows you to insert the same values several times for the same key. I do not know if there are different terms for these two variants. To which one does `MultiMap` apply? May be `MultiMap` for my variant and `MultiSet` for your variant? – Olivier Jacot-Descombes Apr 10 '12 at 13:50
  • I wouldn't have used inheritance at all. Users of this class would want the Dictionary interface completely hidden. You either want a MultiMap or a Dictionary, but not both. – Trap Oct 01 '13 at 10:38
  • This was a quick solution. Of course you could start with a new class and implement `IDictionary` plus some multimap specific things for a perfect solution. Internally you would use a `Dictionary>`. Implementing `IDictionary` requires you to implement 16 properties and methods. As I wrote at the beginning of the article, the presented solution is not perfect. – Olivier Jacot-Descombes Oct 02 '13 at 13:37
  • Additionally, this implementation allows you to add and retrieve whole lists through the original dictionary interface. – Olivier Jacot-Descombes Nov 01 '21 at 14:27
3

Use NameValuedCollection.

Good starting point is here. Straight from the link.

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”);
Sandeep
  • 7,156
  • 12
  • 45
  • 57
  • 1. It's a NameValueCollection - without a 'd' and 2. note that you should use GetValues(String) instead of the indexer - the indexer returns a comma separated string with your values, which is problematic if your values could contain a comma and 3. the collection doesn't distinguish between null as value or null as key-not-found – toong Jul 02 '13 at 10:10
2

Though nearly the same as most of the other responses, I think this is the most efficient and concise way to implement it. Using TryGetValue is faster than using ContainsKey and reindexing into the dictionary as some other solutions have shown.

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);
}
roken
  • 3,946
  • 1
  • 19
  • 32
0

When you add a string, do it differently depending on whether the key exists already or not. To add the string value for the key key:

List<string> list;
if (dictionary.ContainsKey(key)) {
  list = dictionary[key];
} else {
  list = new List<string>();
  dictionary.Add(ley, list);
}
list.Add(value);
Guffa
  • 687,336
  • 108
  • 737
  • 1,005
0

Instead of using a Dictionary, why not convert to an 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

An ILookup is an immutable data structure that allows multiple values per key. Probably not much use if you need to add items at different times, but if you have all your data up-front, this is definitely the way to go.

spender
  • 117,338
  • 33
  • 229
  • 351
0

Here are many variations of the one answer :) My is another one and it uses extension mechanism as comfortable way to execute (handy):

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

}

Then you use it as follows:

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");
Bronek
  • 10,722
  • 2
  • 45
  • 46
0

There is a NuGet package Microsoft Experimental Collections that contains a class MultiValueDictionary which does exactly what you need.

Here is a blog post of the creator of the package that describes it further.

Here is another blog post if you're feeling curious.

Example Usage:

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
Domysee
  • 12,718
  • 10
  • 53
  • 84
0

I was trying to add List to existing key in dictionary and reached the following solution:

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

It will not show any exception and won't replace previous values.

superB
  • 288
  • 2
  • 13
0

There is a 'one-command-line' way to do this using AddOrUpdate from ConcurrentDictionary:

using System.Linq;
using System.Collections.Generic;
using System.Collections.Concurrent;
 
...

var dictionary = new ConcurrentDictionary<string, IEnumerable<string>>();
var itemToAdd = "item to add to key-list";

dictionary.AddOrUpdate("key", new[]{itemToAdd}, (key,list) => list.Append(itemToAdd));

// If "key" doesn't exist, creates it with a list containing itemToAdd as value
// If "key" exists, adds item to already existent list (third parameter)
Fernando Teles
  • 121
  • 2
  • 4
0

I'm improving on this answer with a couple of extension methods I've wrote. The first one is similar to how @Bronek wrote it, just a bit more concise. Simply put, if a key exists, it inserts into the already existing list (assuming it was initialized to begin with). Otherwise, it adds to the list.

public static void AddToList<K, V>(this Dictionary<K, List<V>> multiValueDictionary,
    K key,
    V value)
{
    if (multiValueDictionary.TryGetValue(key, out List<V> lst))
        lst.Add(value);
    else
        multiValueDictionary.Add(key, new List<V> { value });
}

This second function builds off the previous. In System.Linq, the extension method ToDictionary which can transform any IEnumerable into a Dictionary. But what if you have the above scenario where you want to store multiple values for a single key? Well then the below extension will work!

public static Dictionary<K, List<V>> ToDictionaryValueList<T, K, V>(this IEnumerable<T> values, Func<T, K> keySelector, Func<T, V> elementSelector)
{
    var tmp = new Dictionary<K, List<V>>();

    foreach (var val in values)
        tmp.AddToList(keySelector(val), elementSelector(val));

    return tmp;

    // NOTE: You can also use the below IEnumerable extensions to accomplish the same goal, but the above might be (?) more efficient, not sure
    // return values
    //    .GroupBy(v => keySelector(v))
    //    .ToDictionary(v => v.Key, v => v.ToList());
}

With the above two, its now easy to transform any IEnumerable into one of these dictionaries.

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

List<Person> test = new List<Person> 
{
    new Person { Name = "Bob", Age = 22 },
    new Person { Name = "Bob", Age = 28 },
    new Person { Name = "Sally", Age = 22 },
    new Person { Name = "Sally", Age = 22 },
    new Person { Name = "Jill", Age = 22 },
}

// Aggregate each person
Dictionary<string, List<int>> results = test.ToDictionaryValueList((p) => p.Name, (p) => p.Age);

// Use the AddToList extension method to add more values as neeeded
results.AddToList("Jill", 23);

One thing to consider is that duplicate values aren't handled, as expected from a standard List. You'll want to make extension methods for different collections like HashSet if you need it.

GLJ
  • 1,074
  • 1
  • 9
  • 17