Álvaro García
Álvaro García

Reputation: 19356

How to work with dictionaries when I want to add new item?

When I am working with a hashset I can try to add a new item, if the item exists it doesn't no matter. The hashset does not throw an exception and just continues.

With dictionaries, if I want to add a new item, if the key exists, it throws and exception. At the moment I always do that:

Dictionary<long, List<string>> myDic = new Dictionary<long, List<string>>();
long myNewKey = 1;
if(myDic.ConatainsKey(1) == false)
{
    myDic.Add(1, new List<string>());
}
myDic[myNewKey].Add("string01");
myDic[myNewKey].Add("string02");

It makes me repeat many code anytime when I want to add a new value to a dictonary. It makes me wonder if there is better way to work with dictionary.

I was thinking too that I could create a extension method, but I would like to know if there is a better option.

Upvotes: 0

Views: 87

Answers (3)

Dweeberly
Dweeberly

Reputation: 4777

Depends on the type of the dictionary's value (as in key-value). In your example with a long key pointing to a string list the real issue is the need to create a new object value. If the Dictionary was a value type (as in value vs. reference type) pointing to a value type (ex: long->long) you could just use the indexer '[x]'. If you know the range of keys you could initialize your value objects ahead of time. If you are just trying to reduce code you can use an extension method to combine the check, add object and set. Something like this:

public static class Extensions {
    public static void AppendToValues(this Dictionary<long, List<string>> dict, long key, string str){
        if (dict.ContainsKey(key)) {
            dict.Add(key, new List<string>());
            }
        dict[key].Add(str);
        }
    }   

Then in your code call it like so:

myDic.AppendToValues(myNewKey, "string01");

Upvotes: 2

bartonjs
bartonjs

Reputation: 33098

Dictionary<TKey,TValue> has two different ways of adding an item.

void Add(TKey key, TValue value): Because you are calling Add the semantic you are saying is "this is not present, make it be present". So an exception is raised on a duplicate insertion, because it doesn't know if you want the old thing or the new thing.

The indexer (TValue this[TKey key]) supports assignment, which has the semantic of "I want this to be the answer if I call the indexer getter".

dict.Add(1, 1);
var x = dict[1]; // x == 1
dict.Add(1, 2); // throws

dict.Add(1, 1);
var x = dict[1]; // x == 1;
dict[1] = 2;
var y = dict[1]; // x == 1, y == 2.

var dict = new Dictionary<long, int>();
dict[1] = 35;
var x = dict[1]; // x == 35;

ContainsKey is almost always the wrong thing to call. If you are safely reading the value, call TryGetValue, and if you just want to use the dictionary to store whether or not something has been done, use HashSet<T>. (Whose Add method returns a bool (true if Add added something (it didn't exist), false if Add did no work (the value already existed))).

In your specific case of wanting to add things to a list, the TryGetValue approach is the best. Or, using ConcurrentDictionary:

TryGetValue:

List<string> list;

// Read the dictionary exactly once
if (!dict.TryGetValue(key, out list))
{
    list = new List<string>();
    // Write at most once.
    dict[key] = list;
}

list.Add(value);

ConcurrentDictionary:

ConcurrentDictionary<long, List<string>> dict = new ConcurrentDictionary<long, List<string>>();
...
List<string> list = dict.GetOrAdd(key, () => new List<string>());
list.Add(value);

Upvotes: 2

GeorgeT
GeorgeT

Reputation: 504

I don't think you can achieve what you want without an extension method (or a Dictionary subclass, which is an overkill by the way).

You can find a nice implementation of an extension method utilizing Dictionary.TryGetValue (which does not throw exception if a key is not found) here:

Dictionary returning a default value if the key does not exist

If you still don't want to use an extension method, TryGetValue will provide better performance because the lookup of the key is done only once:

Dictionary<long, List<string>> myDic = new Dictionary<long, List<string>>();
long myNewKey = 1;
List<string> list;
if (!myDic.TryGetValue(myNewKey, out list))
{
    list = new List<string>();
    myDic.Add(myNewKey, list);
}

list.Add("string01");
list.Add("string02");

Upvotes: 1

Related Questions