1

I have a simple dictionary call result:

Dictionary<String,String> results=new();
results["a"]="a";
results["b"]="b";
results["c"]="c";

To simplify the example my dictionary only contains 3 letter keys a,b,c. But sometimes it will not contain one of this values or even none, (it will always be initiallized). suppose this situation:

Dictionary<String,String> results=new();
if(anyconditionA) results["a"]="a";
if(anyconditionB)results["b"]="b";
if(anyconditionC)results["c"]="c";

So each time i want to operate with this dictionary i have to check the key value: var test= results["a"]; -> throws System.Collections.Generic.KeyNotFoundException if anycontitionA is not true. so to solve this i do:

if(results.ContainsKey("a"){
  someOperation(results["a"]);
}

so if i have many values code looks like:

if(results.ContainsKey("a"){someOperation(results["a"]);}
if(results.ContainsKey("b"){... stuff}
if(results.ContainsKey("c"){... stuff}
if(results.ContainsKey("..."){... stuff}
if(results.ContainsKey("d"){someOperation(results["d"]);}

¿Is there a propper way to do this in one statement i mean check and do the operation if exists or i have to test every time that value exists? (like do with null operator in a list something like results[a]?.someOperation() ) Thanks!

hesolar
  • 543
  • 4
  • 23
  • 3
    Look at `TryGetValue` and `GetValueOrDefault` , or use a Lookup instead – Caius Jard Oct 29 '21 at 09:37
  • You should be using [`TryGetValue()`](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.dictionary-2.trygetvalue?view=net-5.0) – Matthew Watson Oct 29 '21 at 09:37
  • Read https://softwareengineering.stackexchange.com/questions/396567/is-there-a-better-way-to-use-c-dictionaries-than-trygetvalue – Caius Jard Oct 29 '21 at 09:38

2 Answers2

2

If you find yourself doing this a lot and you want to simplify the calling code, you could write an extension method:

public static class DictionaryExt
{
    public static bool DoIfExists<TKey, TValue>(this Dictionary<TKey, TValue> self, TKey key, Action<TValue> action)
    {
        if (!self.TryGetValue(key, out var value))
            return false;

        action(value);
        return true;
    }
}

Then you could write code like this:

results.DoIfExists("a", someOperation);

results.DoIfExists("b", value => Console.WriteLine("Doing stuff with " + value));

results.DoIfExists("c", value =>
{
    Console.WriteLine("Doing stuff with " + value);
    Console.WriteLine("This uses multiple lines.");
});

I'm really not sure this is worth it (I don't like overuse of extension methods), but that's a matter of opinion!

In my opinion the third example above obfuscates the code and would be better written as:

if (results.TryGetValue("c", out var value))
{
    Console.WriteLine("Doing stuff with " + value);
    Console.WriteLine("This uses multiple lines.");
}

but the first example results.DoIfExists("a", someOperation); is arguably somewhat simpler than:

if (results.TryGetValue("a", out var value))
    someOperation(value);

This is a marginal improvement, and I personally wouldn't bother. It's up to you!

Matthew Watson
  • 104,400
  • 10
  • 158
  • 276
  • 2
    Hah, that's nearly the exact same code I just wrote, then I (also, i guess) concluded that `dictionary.InvokeIfExists(key, someMethod);` didnt really save anything over `if(dictionary.TryGetValue(key, out var x)) someMethod(x)` – Caius Jard Oct 29 '21 at 09:50
  • Yeah perfect, exactly what i need @Matthew Watson – hesolar Oct 29 '21 at 09:54
  • 1
    @Matthew Watson (This is a marginal improvement...) probably in my example but i asked this because im working with code with 100 variables and always was the same situation check-do check-do and it was annoying hahah – hesolar Oct 29 '21 at 10:03
  • 1
    Always worth posting the actual example rather than a contrived one, then we can see if there is some alternative saving/strategy to be made – Caius Jard Oct 29 '21 at 10:20
  • But @CaiusJard dictionary dont contain dictionary.InvokeIfExists – hesolar Oct 29 '21 at 10:37
  • @MatthewWatson dictionary.DoIfExists doesnt exists – hesolar Oct 29 '21 at 10:37
  • 1
    @hesolar `DoIfExists()` is the extension method in `public static class DictionaryExt` above. You need to ensure that you have defined `DictionaryExt` in the same namespace as the code that uses it, or that you specify `using DictionaryExtNamespace` at the top of the file (where `DictionaryExtNamespace` is the namespace in which you put `DictionaryExt`). See [this Microsoft documentation](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/how-to-implement-and-call-a-custom-extension-method) for details, especially the sample code `using CustomExtensions;` – Matthew Watson Oct 29 '21 at 10:41
  • 1
    @hesolar - i know; you have to write it. Actually Matthew already wrote it, you just have to copy that code and paste it into your project somewhere. Usually we do that inside a dedicated class that is for extension methods. Long story short: make a new class file in your project and call it DictionaryExt; VS will add some mostly-empty class file with some namespace like `namespace Whatever{ class DictionaryExt { } }`, delete the `class DictionaryExt { }` and paste in the contents of the very first code block in Matthew's answer. Then everything will work – Caius Jard Oct 29 '21 at 11:11
2

You've got the "maybe don't call an operation that takes the value, if the key is not in the dictionary" from Matthew Watson, but at the end of your question you asked about a slightly different problem

¿Is there a propper way to do this in one statement i mean check and do the operation if exists or i have to test every time that value exists? (like do with null operator in a list something like results[a]?.someOperation() ) Thanks!

If the operation is a method on the value in the dictionary then sure, you can use ?. to prevent a null ref on a non existent value:

        var dictionary = new Dictionary<int, StringBuilder>();

        dictionary.GetValueOrDefault(1)?.AppendLine("hello");

GetValueOrDefault is implemented as an extension method and is a .net core 2.2+ thing. There are nuget packages that make it available for older versions, or you can write it yourself as an extension possibly adapting it from the netcore source and putting it into your app:

    public static TValue? GetValueOrDefault<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, TKey key)
    {
        return dictionary.GetValueOrDefault(key, default!);
    }

    public static TValue GetValueOrDefault<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, TKey key, TValue defaultValue)
    {
        if (dictionary == null)
        {
            throw new ArgumentNullException(nameof(dictionary));
        }

        TValue? value;
        return dictionary.TryGetValue(key, out value) ? value : defaultValue;
    }
Caius Jard
  • 72,509
  • 5
  • 49
  • 80