Dictionary TryGetValue with int values, how to avoid double lookup
Asked Answered
R

3

9

When doing something like:

int value;
if (dict.TryGetValue(key, out value))
{
    if (condition)
    {
        //value = 0;  this copies by value so it doesn't change the existing value
        dict[key] = 0;
    }
}
else
{
    dict[key] = 0;
}

Is there any way I can avoid index lookup to replace the existing value? I'm already verifying the key exists using TryGetValue so it seems like a waste to have to retrieve value by index again.

On a separate note, as in the else{} part of my code, is it generally considered good practice to use the indexer when adding new or replacing old values, and add to make it clear you are adding and not replacing? Or should I just use the indexer every time? The way I have learned to use dictionary, I always do a TryGetValue lookup and in the else portion I handle cases where no key exists.

Recollect answered 9/4, 2013 at 13:49 Comment(8)
you aren't looking it up twice.Soelch
@DanielA.White: Using the indexer does a lookup IIRC ;pCrowe
does your (condition) depend on value?Bender
@G.Stoynev yes, it does a comparisonRecollect
if (!dict.TryGetValue(key, out value) || value > 0) { dict[key] = 0; } you can use value, because you'll only get past the || if the TryGet worked.Unwept
It sounds almost like you want a reference to the bucket the value will be stored. This is not exposed in .NET. Also you would have to deal with a valuetype bucket which could be tricky.Crowe
Regarding your minor question, I personally would prefer dict.Add(key, 0) over dict[key] = 0 in cases where you know the key is new.Alvaalvan
Related: How to increase or decrease numeric value of an existing Dictionary key in one line.Micronucleus
P
6

Is there any way I can avoid index lookup to replace the existing value?

Not that I know of - but dictionary access should be very fast unless you have a custom class that overriddes GetHashCode poorly.

If you're not seeing a performance problem because of the double lookup I'd leave it alone.

Patras answered 9/4, 2013 at 13:53 Comment(1)
Thank you, it's not leading to a performance problem but since this pattern comes up frequently for me I figured I'd ask once and learn the optimal way of doing it.Recollect
B
2

You can try this out

Object value;
if (dict.TryGetValue(key, out value))
{
    if (condition)
    {
        //value.data = 0;  this copies by value so it doesn't change the existing value
        value.data = 0;
    }
}
else
{
    value.data = 0;
}

The essense of story is, the type you are fetching out is a generic type and is allocated on heap. i.e. when you fetch it out, it will come out as value. However, if you fetch out object, it will be a reference to the original allocated object and you can modify the value of a particular property of object.

Bareback answered 9/4, 2013 at 14:5 Comment(0)
S
-1

I prefer to define convenience extension methods for things like this. For example:

    public static TValue GetValueOrDefault<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, TValue defaultValue)
    {
        TValue value;
        return dictionary.TryGetValue(key, out value) ? value : defaultValue;
    }

    public static TValue GetOrSet<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, TValue value)
    {
        return dictionary[key] = dictionary.GetValueOrDefault(key, value);
    }

There is little need to worry about performance of dictionary hashing & lookups - I am primarily concerned with readability and maintainability. With the above extension methods, this kind of thing is a one-liner:

int value = dict.GetOrSet(key, 0);

(Disclaimer: doesn't perform the if (condition) check - I rarely experience these scenarios)

Shape answered 9/4, 2013 at 13:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.