20

I have a few values that I want to put into a Dictionary:

// Pretend these values are retrieved from a database or something
string firstName = "John";
string lastName = "Smith";

List<string> listHobbies = new List<string> () {"hockey","soccer"};

var dict = new Dictionary<string, dynamic>() 
{
    {"firstName", firstName},
    {"lastName", lastName},
    {"hobbies", listHobbies}
};

However, in some instances, the List<string> variable may be an empty list. If this is the case, I do not want to add the {"hobbies", listHobbies} key to the resulting dictionary.

Is there a way to implement this check within the collection initializer of the dictionary? or am I stuck using an if statement to check the size of the list and remove the key immediately after the declaration?

Edit: Essentially, I am wondering if C# allows something like this:

var dict = new Dictionary<string, dynamic>() 
{
    {"firstName", firstName},
    {"lastName", lastName},
    {"hobbies", listHobbies} unless (listHobbies.Count == 0) // don't add the "hobbies" key if the list is empty.
};

I can currently achieve this by doing something like this:

var dict = new Dictionary<string, dynamic>() 
{
    {"firstName", firstName},
    {"lastName", lastName},
    {"hobbies", listHobbies}
};
}.Concat(listHobbies != 0 ? new Dictionary<string, dynamic>() { { "hobbies", listHobbies } } : new Dictionary<string, dynamic>()).ToDictionary(k => k.Key, v => v.Value);

But this is incredibly ugly.

Edit 2: I've found a somewhat acceptable solution, if anyone else can offer a nicer looking alternative, feel free to suggest:

var dict = new Dictionary<string, dynamic>() 
{
    {"firstName", firstName},
    {"lastName", lastName},
    {"hobbies", !listHobbies.Any() ? listHobbies : null}
}
.Where(entry => entry.Value != null)
.ToDictionary(k => k.Key, v => v.Value);
5
  • how about checking if (listHobbies.Count() > 0) Commented Apr 11, 2014 at 4:07
  • yes I can check that condition either before or after the Dictionary declaration and remove (or just not add) the key accordingly, however I am looking for a way to do this within the dictionary's initialization for the sake of clean code. Commented Apr 11, 2014 at 4:08
  • I've got a feeling that putting a conditional in a collection initalizer might be bad practice. we use collection initializers when we are sure of the values, in other cases it might be more advisable to run a loop with conditional. The Where(x=>x!=null) will cause the whole collection to be iterated again for the null check. Commented Apr 11, 2014 at 4:35
  • 1
    The feeling i get is that you don't need a Dictionary<string, dynamic>. You need an object that has 3 properties: firstName, lastName and hobbies. You can then stuff this object into a Dictionary or some other collection. Commented Apr 11, 2014 at 4:37
  • The solution you came up with in your Edit2 is a good one. It clearly communicates that you want to start with an initial collection and filter out bad items. Commented May 19, 2021 at 9:18

2 Answers 2

12

You can

C#'s collection-initializers are just syntactic-sugar over any method named Add, including extension-methods. Whatever C# syntax you have between each-item's {} will be translated into Add method parameters.

So these two statements are almost identical (There is a slight difference in collection-object-lifetime if an exception is thrown before the initializer completes, but that's not worth worrying about):

// Manually:
Dictionary<String,Foo> dict = new Dictionary<String,Foo>();
dict.Add( "foo1", new Foo(1) );
dict.Add( "foo2", new Foo(2) );
dict.Add( "foo3", new Foo(3) );
dict.Add( "foo4", new Foo(4) );

// Collection-initializer:
Dictionary<String,Foo> dict = new Dictionary<String,Foo>()
{
    { "foo1", new Foo(1) },
    { "foo2", new Foo(2) },
    { "foo3", new Foo(3) },
    { "foo4", new Foo(4) }
};

(This also means that if you thought object-initializers were somehow more-efficient than repetitive calls to .Add() or that they'd even result in C++ constexpr-style compile-time pre-initialization of a collection... well unfortunately you're wrong: collection-initializers always have at least O(n) runtime - if you need more efficient initialization consider using an array-backed data-structure so you can use a pre-init array).

So if you want a conditional initializer, first define the extension method for your collection-type (in this case Dictionary<String,dynamic>) with an extra Boolean condition parameter:

public static class MyDictionaryExtensions
{
    public static void Add( this Dictionary<String,dynamic> dict, String key, dynamic value, Boolean condition )
    {
        if( condition )
        {
            dict.Add( key, value );
        }
    }
}

Then you can use it like so:

// Pretend these values are retrieved from a database or something
string firstName = "John";
string lastName = "Smith";

List<string> listHobbies = new List<string> () {"hockey","soccer"};

var dict = new Dictionary<string, dynamic>() 
{
    { "firstName", firstName   },
    { "lastName" , lastName    },
    { "hobbies"  , listHobbies, listHobbies.Count > 0 }
};

Better yet: you can make it generic (and you probably shouldn't be using dynamic anyway):

public static class MyDictionaryExtensions
{
    public static void Add<TKey,TValue>( this Dictionary<TKey,TValue> dict, TKey key, TValue value, Boolean condition )
        where TKey : notnull
    {
        if( condition )
        {
            dict.Add( key, value );
        }
    }
}

Here's screenshot proof of it working in LinqPad:

enter image description here


But why stop there? You're not limited to Boolean condition parameters: you could have some gnarly logic that inspects the state of the this dict, for example, or use a Func<Boolean> to determine the condition at runtime for each item.

There is a minor caveat though: your Add extension method's parameters cannot exactly match the original Add method, otherwise your extension-method won't be selected due to how extension-methods always have lower precedence. You also cannot force-select your extension-method by using an explicit parameter name (collection-initializers don't have named parameters), but you could use a dummy parameter as a third-parameter using a custom struct type (so it has effectively zero cost).

For example, here's an extension I use to succinctly populate Exception.Data's dictionary with information from a failed HttpClient request, which skips responseBody if the value is null:

internal readonly struct OnlyIfBodyIsNotNull
{
}

internal static class DictionaryExtensions
{
    public static void Add( this IDictionary exDataDict, String key, String? body, OnlyIfBodyIsNotNull _ )
    {
        if( body != null )
        {
            exDataDict.Add( key, body );
        }
    }
}

Used like so:

public static InvalidOperationException CreateException( String message, HttpResponseHeaders responseHeaders, HttpContentHeaders contentHeaders, String? responseBody = null )
{
    return new InvalidOperationException( message )
    {
        Data =
        {
            { "Response.Headers"       , responseHeaders.ToString() },
            { "Response.ContentHeaders", contentHeaders .ToString() },
            { "Response.Body"          , responseBody              , default(OnlyIfBodyIsNotNull) }
        }
    };
}
Sign up to request clarification or add additional context in comments.

2 Comments

Great answer. I was aware about the syntactic sugar of collection initialization, but I really didn't know you could write your own in form of extension methods.
This is a non-readable hack. Just call add instead of being smart.
1

What's wrong with this?

var dict = new Dictionary<string, dynamic>() 
{
    {"firstName", firstName},
    {"lastName", lastName}
};
if (listHobbies.Any())
    dict.Add("hobbies", listHobbies);

4 Comments

It mutates the collection after declaration, which is something I was trying to avoid. What I'm trying to do however doesn't appear to be currently possible in C#, but I think I have found a nicer looking alternative, will edit the OP in a minute.
@Fano why not just move the mutation part into a method so and make the dict readonly thus you will still be able to maintain immutability. Is there any specific reason why the collection should not be changed after initalisation?
No particular reason, I've been learning about functional programming principles and trying to apply them to my other projects, it's probably not best practice to do it like this in C#, a language that wasn't created with such things in mind (I imagine). Nonetheless, I was just curious if this was possible.
Also, this approach won't work if the local is typed as IReadOnlyDictionary<String,dynamic>.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.