James Ko
James Ko

Reputation: 34609

Best way to check if a key exists in a Dictionary before adding it?

When getting a key from a Dictionary you're not sure exists, you would usually use TryGetValue instead of ContainsKey + the get indexer to avoid the overhead of checking the key twice. In other words, this:

string password;
if (accounts.TryGetValue(username, out password))
{
    // use the password
}

would be preferred to this:

if (accounts.ContainsKey(username))
{
    string password = accounts[username];
}

What if I wanted to check if a key already existed before setting it to a value? For example, I would want to check if a username existed before overwriting it with a new password:

if (!accounts.ContainsKey(username))
{
    accounts.Add(username, password);
}
else
{
    Console.WriteLine("Username is taken!");
}

vs

// this doesn't exist
if (!accounts.TrySetValue(username, password))
{
    Console.WriteLine("Username is taken!");
}

Is there a more performant alternative to ContainsKey and Add that does this?

Upvotes: 15

Views: 60017

Answers (4)

MaximGurschi
MaximGurschi

Reputation: 21

I know i am late to this, but you can use a trick and store the count before the indexer set and check the count after the indexer set. If the counts are the same then you have overridden the key otherwise you have added a new mapping:

public static bool AddOrUpdate<TKey, TValue>(this IDictionary<TKey, TValue> 
    dictionary, TKey key, TValue value)
{
    var countBefore = dictionary.Count;
    dictionary[key] = value;
    return countBefore != dictionary.Count;
}

Upvotes: 2

Scott Chamberlain
Scott Chamberlain

Reputation: 127603

If you think inserting a new name will be the common case and attempting to insert a duplicate will be the rare case you may just want to use the overhead of catching a exception.

try
{
    accounts.Add(username, password);
}
catch (ArgumentException)
{
    Console.WriteLine("Username is taken!");
}

If you call Add with a existing key a ArgumentException will be thrown. Even if you have frequent duplicates this will still be likely more performant than your ContainsKey check.

Upvotes: 6

Enigmativity
Enigmativity

Reputation: 117154

I tend to write my own extensions as needed.

For example, GetValueOrDefault like this:

public static V GetValueOrDefault<K, V>(this IDictionary<K, V> @this, K key, Func<V> @default)
{
    return @this.ContainsKey(key) ? @this[key] : @default();
}

It can be used like this:

var password = accounts.GetValueOrDefault(username, () => null);
if (password != null)
{
    //do stuff
}

Or SetValueIfExists:

public static V SetValueIfExists<K, V>(this IDictionary<K, V> @this, K key, V value)
{
    if (@this.ContainsKey(key))
    {
        @this[key] = value;
    }
}

Or SetValueIfNotExists:

public static V SetValueIfNotExists<K, V>(this IDictionary<K, V> @this, K key, V value)
{
    if ([email protected](key))
    {
        @this[key] = value;
    }
}

Upvotes: 5

Backs
Backs

Reputation: 24913

If you don't want to override, I think it's better to write your own extension-method like TryGetValue. There is no standard method.

OR

Use CuncurrentDictionary, it has TryAdd method, but you'll have overhead on sync.

So, simple answer - no, there is no such method.

Upvotes: 5

Related Questions