Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Combined "Check Add or Fetch" from Dictionary

I'm tired of this dictionary idiom:

        Dictionary<Guid,Contact> Contacts;
        //...
        if (!Contacts.ContainsKey(id))
        {
            contact = new Contact();
            Contacts[id] = contact;
        }
        else
        {
            contact = Contacts[id];
        }

It would be nice if there was a syntax that permitted the new value to be created implicitly from a default constructor if it does not exist (the dictionary knows the type of the value, after all). Anyone seen a helper (such as an extension method) that does this?

like image 216
Brent Arias Avatar asked Sep 14 '10 03:09

Brent Arias


2 Answers

Implementation:

public static TValue GetOrAdd<TKey, TValue>(this IDictionary<TKey, TValue> dictionary,
                                            TKey key, Func<TValue> valueCreator)
{
    TValue value;
    if (!dictionary.TryGetValue(key, out value))
    {
        value = valueCreator();
        dictionary.Add(key, value);
    }
    return value;
}

public static TValue GetOrAdd<TKey, TValue>(this IDictionary<TKey, TValue> dictionary,
                                            TKey key) where TValue : new()
{
   return dictionary.GetOrAdd(key, () => new TValue());
}

Usage:

var contacts = new Dictionary<Guid, Contact>();
Guid id = ...

contacts.GetOrAdd(id).Name = "Abc"; // ok since Contact has public parameterless ctor
contacts.GetOrAdd(id, () => new Contact { Name = "John Doe" }).Age = 40;
like image 170
Ani Avatar answered Oct 17 '22 04:10

Ani


Same as Ani's answer, but in a more unintelligible one-liner :)

/// <param name="valueCreator">The expensive value creator function.</param>
public static T GetOrAdd<S, T>(this IDictionary<S, T> dict, S key, Func<T> valueCreator)
{
    return dict.TryGetValue(key, out var value) ? value : dict[key] = valueCreator();
}

Provide a delegate as value creator than value itself to prevent unnecessary object creation.

Dictionary, unfortunately, doesn't have this feature out of the box to do all this in a single lookup.

like image 39
nawfal Avatar answered Oct 17 '22 03:10

nawfal