{"id":283,"date":"2014-06-20T14:30:00","date_gmt":"2014-06-20T14:30:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/dotnet\/2014\/06\/20\/would-you-like-a-multidictionary\/"},"modified":"2021-09-30T16:48:53","modified_gmt":"2021-09-30T23:48:53","slug":"would-you-like-a-multidictionary","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/would-you-like-a-multidictionary\/","title":{"rendered":"Would you like a MultiDictionary?"},"content":{"rendered":"<p style=\"padding-left: 30px\"><em>We&rsquo;ve recently shipped new collection types on NuGet with our Immutable Collections package. NuGet allows us to ship prerelease and experimental versions of libraries to gather feedback from the community. In this post, our <strong>software developer intern Ian Hays<\/strong> will talk about his intern project: an experimental NuGet package containing advanced collection types. &#8212; Immo<\/em><\/p>\n<p>Dictionary provides a mapping between a key and a single value, and is one of the most used collection types in the .NET Framework. Programs often need a mapping between one key and multiple values. While the functionality can be composed using existing collection types, it can be error prone due to corner cases.<\/p>\n<p>Today we&rsquo;re <a href=\"https:\/\/www.nuget.org\/packages\/Microsoft.Experimental.Collections\/\">releasing an experimental NuGet package<\/a> with a new related type, <code>MultiDictionary<\/code>. The <code>MultiDictionary<\/code> is a simple, intuitive collection that essentially functions like a <code>Dictionary&lt;TKey, ICollection&lt;TValue&gt;&gt;<\/code> but abstracts the <code>ICollection&lt;TValue&gt;<\/code>. A more precise definition is that it is a <code>Dictionary&lt;TKey, TValue&gt;<\/code> that allows multiple <code>TValues<\/code> to be added for any <code>TKey<\/code> (i.e. keys don&rsquo;t have to be unique).<\/p>\n<h2>What&rsquo;s wrong with Dictionary?<\/h2>\n<p>The .NET Framework already includes an efficient dictionary implementation that can be used with an <code>ICollection<\/code> as the value type parameter, so why bother making <code>MultiDictionary<\/code> at all? The short answer is simplicity. The long answer is also simplicity.<\/p>\n<p>What&rsquo;s your favorite data structure? Mine is the dictionary; I love the near constant time operations, the huge number of use cases, the cleanliness! Although the dictionary has a wide variety of uses, there are times when I want to add multiple values per key and Dictionary just doesn&rsquo;t quite cut it. In those situations the solution is simple: just build a <code>Dictionary&lt;TKey, List&lt;TValue&gt;&gt;<\/code>!<\/p>\n<p>The issue with the dictionary of list is nearly every call to the <code>Dictionary<\/code> has to be wrapped in logic to check the current state of the dictionary before adding\/removing\/indexing etc. I&rsquo;m never satisfied with the idea of surrounding my dictionary calls with a series of <code>if<\/code> statements, so I end up coding an entirely new data structure to wrap my dictionary of lists. I&rsquo;ve had to do this more times than I&rsquo;m proud of, which is why I&rsquo;m pleased to code up that data structure <em>just one last time<\/em>.<\/p>\n<h2>Introducing MultiDictionary<\/h2>\n<p>I could go into detail on the API and characteristics of <code>MultiDictionary<\/code>, but I&rsquo;ll save that for later; let&rsquo;s first look at some examples of typical usage for <code>MultiDictionary<\/code>.<\/p>\n<pre><code>MultiDictionary&lt;string, int&gt; myDictionary = new MultiDictionary&lt;string, int&gt;();<br \/>myDictionary.Add(\"key\", 1);<br \/>myDictionary.Add(\"key\", 2);<br \/>myDictionary.Add(\"key\", 3);<br \/>\/\/myDictionary[\"key\"] now contains the values 1, 2, and 3<\/code><\/pre>\n<p>When we index into our <code>myDictionary<\/code>, we get an <code>ICollection&lt;int&gt;<\/code> that contains the elements 1, 2, and 3. If the key wasn&rsquo;t in the <code>MultiDictionary<\/code>, then an empty <code>ICollection<\/code> associated with that key will be returned.<\/p>\n<p>All <code>ICollection<\/code> instances returned by indexing into the <code>MultiDictionary<\/code> function as indirections to the collections inside of our <code>MultiDictionary<\/code>, which means that as the <code>MultiDictionary<\/code> changes so does the <code>ICollection<\/code> and vice versa. Consider the following example that illustrates this:<\/p>\n<pre><code>MultiDictionary&lt;string, int&gt; myDictionary = new MultiDictionary&lt;string, int&gt;();<br \/>myDictionary.Add(\"key\", 1);<br \/>ICollection&lt;int&gt; myCollection = myDictionary[\"key\"];<br \/>myCollection.Add(2);<br \/>\/\/myCollection now contains the values 1, and 2<\/code><\/pre>\n<p>The <code>MultiDictionary<\/code> also has methods for adding or removing one key-value pair at a time as well as adding or removing multiple values per key.<\/p>\n<pre><code>MultiDictionary&lt;string, int&gt; myDictionary = new MultiDictionary&lt;string, int&gt;();<br \/>myDictionary.AddRange(\"key1\", new int[] { 1, 2, 3 });<br \/>myDictionary.AddRange(\"key2\", new int[] { 1, 2, 3 });<br \/>myDictionary.Remove(\"key1\");<br \/>myDictionary.RemoveItem(\"key2\", 2);<br \/>\/\/myDictionary now contains key2 with values 1 and 3<\/code><\/pre>\n<p>There are a few more interesting and useful methods inside of the <code>MultiDictionary<\/code>, but I&rsquo;ll let you explore those on your own!<\/p>\n<h2>Why should I use MultiDictionary?<\/h2>\n<p>Let&rsquo;s look at some benefits of the <code>MultiDictionary<\/code>:<\/p>\n<ul>\n<li>\n<p>Adding a single key-value pair is far simpler with a <code>MultiDictionary<\/code> than with a Dictionary of lists<\/p>\n<pre><code>\/\/Adding with a MultiDictionary&lt;TKey,TValue&gt;<br \/>myDictionary.Add(1, 2);<br \/><br \/>\/\/Adding with a Dictionary&lt;TKey, ICollection&lt;TValue&gt;&gt;<br \/>if (singleDictionary.ContainsKey(1))<br \/>&nbsp;&nbsp;&nbsp;&nbsp;singleDictionary[1].Add(2);<br \/>else<br \/>&nbsp;&nbsp;&nbsp;&nbsp;singleDictionary.Add(1, new int[] { 2 });<\/code><\/pre>\n<\/li>\n<li>\n<p>Adding multiple values to a Key is supported in the <code>MultiDictionary<\/code> through the <code>AddRange<\/code> method<\/p>\n<pre><code>\/\/Adding multiple values with a MultiDictionary&lt;TKey,TValue&gt;<br \/>myDictionary.AddRange(1, new int[] { 1, 2, 3 });<br \/><br \/>\/\/Adding multiple values with a Dictionary&lt;TKey, ICollection&lt;TValue&gt;&gt;<br \/>ICollection&lt;int&gt; singleDictionaryCollection;<br \/>if (singleDictionary.TryGetValue(1, out singleDictionaryCollection))<br \/>{<br \/>&nbsp;&nbsp;&nbsp;&nbsp;foreach (int toAdd in (new int[] { 1, 2, 3 }))<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;singleDictionaryCollection.Add(toAdd);<br \/>}<br \/>else<br \/>{<br \/>&nbsp;&nbsp;&nbsp;&nbsp;singleDictionary.Add(1, new int[] { 1, 2, 3 });<br \/>}<\/code><\/pre>\n<\/li>\n<li>\n<p>Indexing into the <code>MultiDictionary<\/code> will never throw an exception (unless the key is <code>null<\/code>) and will always return an <code>ICollection<\/code> that changes as the <code>MultiDictionary<\/code> changes and vice versa.<\/p>\n<\/li>\n<li>\n<p>You can remove a single key value pair or all of the values associated with a key through the <code>RemoveItem<\/code> and <code>Remove<\/code> methods, respectively.<\/p>\n<\/li>\n<li>\n<p>The <code>Values<\/code> property returns an <code>ICollection&lt;TValue&gt;<\/code> instead of an <code>ICollection&lt;ICollection&lt;TValue&gt;&gt;<\/code> like a <code>Dictionary&lt;TKey, ICollection&lt;TValue&gt;&gt;<\/code> would. This makes it easier to iterate through the values in the dictionary.<\/p>\n<\/li>\n<\/ul>\n<h2>Try it out!<\/h2>\n<p>Enough reading, try it out in your favorite .NET language and let us know what you think! The Alpha release of the <code>MultiDictionary<\/code> is available <a href=\"https:\/\/www.nuget.org\/packages\/Microsoft.Experimental.Collections\/\">on NuGet<\/a>. Please let us know what you think by leaving a comment on this post or by contacting us via the <a href=\"https:\/\/www.nuget.org\/packages\/Microsoft.Experimental.Collections\/1.0.1-alpha\/ContactOwners\">contact page<\/a>.<\/p>\n<p>Thanks for reading, and enjoy!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>We&rsquo;ve recently shipped new collection types on NuGet with our Immutable Collections package. NuGet allows us to ship prerelease and experimental versions of libraries to gather feedback from the community. In this post, our software developer intern Ian Hays will talk about his intern project: an experimental NuGet package containing advanced collection types. &#8212; Immo [&hellip;]<\/p>\n","protected":false},"author":335,"featured_media":58792,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[685],"tags":[30,43,50,51,104,110,117],"class_list":["post-283","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dotnet","tag-announcement","tag-bcl","tag-collections","tag-community","tag-nuget","tag-portable-class-libraries","tag-releases"],"acf":[],"blog_post_summary":"<p>We&rsquo;ve recently shipped new collection types on NuGet with our Immutable Collections package. NuGet allows us to ship prerelease and experimental versions of libraries to gather feedback from the community. In this post, our software developer intern Ian Hays will talk about his intern project: an experimental NuGet package containing advanced collection types. &#8212; Immo [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/283","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/users\/335"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/comments?post=283"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/283\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/58792"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=283"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=283"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=283"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}