23

Anyone know of a good implementation of a MultiValueDictionary? Basically, I want something that allows multiple values per key. I want to be able to do something like

dict.Add(key, val);

And if the key doesn't already exist, it will add it, if it does, it will just add another value to that key. I'm just going to iterate over it, so I don't really care about the other retrieval methods.

ekostadinov
  • 6,880
  • 3
  • 29
  • 47
mpen
  • 272,448
  • 266
  • 850
  • 1,236
  • possible duplicate of [dictionary-one-key-many-values?](http://stackoverflow.com/questions/2101069/c-sharp-dictionary-one-key-many-values?lq=1) – nawfal May 19 '14 at 12:31
  • 1
    Take a look at the LookUp class https://msdn.microsoft.com/en-us/library/bb460184(v=vs.110).aspx – Yair Halberstadt Sep 12 '17 at 16:56

10 Answers10

46

Microsoft just added an official prelease version of exactly what you're looking for (called a MultiValueDictionary) available through NuGet here: https://www.nuget.org/packages/Microsoft.Experimental.Collections/

Info on usage and more details can be found through the official MSDN blog post here: http://blogs.msdn.com/b/dotnet/archive/2014/06/20/would-you-like-a-multidictionary.aspx

I'm the developer for this package, so let me know either here or on MSDN if you have any questions about performance or anything.

Hope that helps.

Eliahu Aaron
  • 4,103
  • 5
  • 27
  • 37
Ian Hays
  • 659
  • 1
  • 6
  • 4
25

You can easily make one from a dictionary of lists:

public class MultiValueDictionary<Key, Value> : Dictionary<Key, List<Value>> {

  public void Add(Key key, Value value) {
    List<Value> values;
    if (!this.TryGetValue(key, out values)) {
      values = new List<Value>();
      this.Add(key, values);
    }
    values.Add(value);
  }

}
Guffa
  • 687,336
  • 108
  • 737
  • 1,005
10

It doesn't exist, but you can build one pretty quickly from Dictionary and List:

class MultiDict<TKey, TValue>  // no (collection) base class
{
   private Dictionary<TKey, List<TValue>> _data =  new Dictionary<TKey,List<TValue>>();

   public void Add(TKey k, TValue v)
   {
      // can be a optimized a little with TryGetValue, this is for clarity
      if (_data.ContainsKey(k))
         _data[k].Add(v);
      else
        _data.Add(k, new List<TValue>() { v}) ;
   }

   // more members
}
Stefan Steiger
  • 78,642
  • 66
  • 377
  • 442
H H
  • 263,252
  • 30
  • 330
  • 514
  • Lists don't have very efficient inserts, do they? Don't they use an array internally? – mpen Oct 03 '10 at 18:23
  • Yes they use an array, and sometimes a List.Add() will need to re-allocate. But mostly Add is O(1). – H H Oct 03 '10 at 18:27
  • 4
    @Mark: `List.Add` has [amortized](http://en.wikipedia.org/wiki/Amortized) O(1) performance, and is generally *faster* than `LinkedList.AddLast` because it requires fewer operations. `LinkedList.AddLast` might make sense if you want to *never* have an O(n) resize happen (even though it will happen rarely on a `List` that you're only adding to). – Dan Tao Oct 03 '10 at 18:31
  • @Dan: Ah.. so you're saying the "constant" times in the LinkedList generally outweigh the performance of a List? – mpen Oct 03 '10 at 18:34
  • 4
    @Mark: Yes. I've seldom seen cases where it made sense to choose a `LinkedList` over a `List` when you're never inserting/removing from the middle (that's where `LinkedList` really shines). When you're just adding to the *end*, `LinkedList` is just costing you a lot more (e.g., creation of `LinkedListNode` objects => more fields to initialize => elimination of arrays' performance advantage due to extremely optimized memory access) for very little benefit: one `LinkedList.AddLast` in a blue moon will outperform a single `List.Add`. The rest will be the opposite. – Dan Tao Oct 03 '10 at 18:39
  • @Dan: Good to know. I'll stop using LinkedList then :) – mpen Oct 03 '10 at 18:54
  • 2
    List is also more cache friendly compared to linked list – naiem Dec 06 '12 at 13:15
4

You can always use a Tuple for your second generic parameter:

var dict = new Dictionary<string,Tuple<string,int,object>>();
dict.Add("key", new Tuple<string,int,object>("string1", 4, new Object()));

Or even , a generic List as a second generic parameter:

var dict = new Dictionary<string,List<myType>>();

That will allow you to bind multiple values to a single key.

For ease of use, you can create an extension method that will check for existence of a key and addition to the list.

Oded
  • 489,969
  • 99
  • 883
  • 1,009
  • Yeah, but then I have to check if the key exists before adding to it every time. I want the collection class to handle that automatically. – mpen Oct 03 '10 at 18:13
  • You can inherit from Dictionary, overriding `add` to do this check. It is not resource intensive, as checking for a key is pretty quick. Don't optimize before you _need_ to. – Oded Oct 03 '10 at 18:15
  • No one said anything about optimization, this is just about convenience. And if I inherit from dictionary, I'm pretty sure I would have to `new` the `Add` method, not `override` which I generally don't like to do because it breaks inheritance. Also, the method signatures need to be slightly different, because some them should actually return lists of TValues, rather than just a TValue. Lastly, I'm not really sure why you've suggested a Tuple.. those aren't expandable? – mpen Oct 03 '10 at 18:22
  • @Mark - You say "multiple values for key". It wasn't clear that the values need to be expandable. See my update, regarding the second generic type being a `List`. – Oded Oct 03 '10 at 18:24
  • @Oded: That's why I wrote the paragraph below ;) "it will just add another value to that key" re: update: An extension method is a clever solution... I wanted the enumerator to return KeyValuePairs though, not Key,List pairs though. – mpen Oct 03 '10 at 18:31
  • @Mark - you can always use a `Dictionary` as your generic parameter, instead of a `List` (`Dictionary>`), as a Dictionary is in essence a set of KVPs. – Oded Oct 03 '10 at 18:35
  • @Mark - I've seen `List>>` in the past... Not pretty, but did the job. – Oded Oct 03 '10 at 18:54
4

Here's one I wrote a while back that you can use.

It has a "MultiValueDictionary" class that inherits from Dictionary.

It also has an extension class that allows you to use the special Add functionality on any Dictionary where the value type is an IList; that way you're not forced to use the custom class if you don't want to.

public class MultiValueDictionary<KeyType, ValueType> : Dictionary<KeyType, List<ValueType>>
{
    /// <summary>
    /// Hide the regular Dictionary Add method
    /// </summary>
    new private void Add(KeyType key, List<ValueType> value)
    {            
        base.Add(key, value);
    }

    /// <summary>
    /// Adds the specified value to the multi value dictionary.
    /// </summary>
    /// <param name="key">The key of the element to add.</param>
    /// <param name="value">The value of the element to add. The value can be null for reference types.</param>
    public void Add(KeyType key, ValueType value)
    {
        //add the value to the dictionary under the key
        MultiValueDictionaryExtensions.Add(this, key, value);
    }
}

public static class MultiValueDictionaryExtensions
{
    /// <summary>
    /// Adds the specified value to the multi value dictionary.
    /// </summary>
    /// <param name="key">The key of the element to add.</param>
    /// <param name="value">The value of the element to add. The value can be null for reference types.</param>
    public static void Add<KeyType, ListType, ValueType>(this Dictionary<KeyType, ListType> thisDictionary, 
                                                         KeyType key, ValueType value)
    where ListType : IList<ValueType>, new()
    {
        //if the dictionary doesn't contain the key, make a new list under the key
        if (!thisDictionary.ContainsKey(key))
        {
            thisDictionary.Add(key, new ListType());
        }

        //add the value to the list at the key index
        thisDictionary[key].Add(value);
    }
}
Doctor Jones
  • 21,196
  • 13
  • 77
  • 99
  • Of course... inherit from a dict with a list instead! Clever. What's that `new()` bit do? Don't think I've seen that before. – mpen Oct 03 '10 at 18:28
  • Taken from MSDN; Use the new modifier to explicitly hide a member inherited from a base class. To hide an inherited member, declare it in the derived class using the same name, and modify it with the new modifier. – Doctor Jones Oct 03 '10 at 18:30
  • 1
    @DoctaJonez - I believe is he asking about the generic parameter constraint (the only `new()` in the code). @Mark - it is a [generic parameter constraint](http://msdn.microsoft.com/en-us/library/d5x73970.aspx) requiring the passed in generic type to have a public parameterless constructor. – Oded Oct 03 '10 at 18:32
  • 1
    @DoctaJonez: I think @Mark means your `where ListType : new()` constraint. (@Mark: That means `ListType` must have a public parameterless constructor. Also, I'm surprised you would like this answer which hides the `base.Add` method as it seems you disliked that very aspect of Oded's answer.) – Dan Tao Oct 03 '10 at 18:35
  • @Dan: Yes.. I noticed my hypocritcalness too. I still don't love it, but at least in this case he made it private and added a 2nd method with a different signature... which still doesn't fix the inheritance problem, but... I dunno! I'm trying to find the lesser of evils. @Oded: Yes, that's what I was referring too. Thanks! – mpen Oct 03 '10 at 18:50
  • @Mark: Go with Henk's answer. It isn't evil at all ;) – Dan Tao Oct 03 '10 at 18:54
  • This is way, way late, but you could remove the `where ListType: new()` constraint by having a `Func` parameter. Then instead of doing `thisDictionary.Add(key, new ListType());`, you could do `thisDictionary.Add(key, func());`, and a consumer could do something like `() => new MyCustomList(args)`. Then you wouldn't have to inherit from `Dictionary`-- you could inherit from `Dictionary>` – David Schwartz Mar 17 '15 at 18:00
  • the only problem I have with this is that if somehow you pass this as IDictionary Add(key, value) will call the base - to successfully make this work you'll have to create a trully overridable dictionary just like the Collection or go through Dictionary base class - I myself have created a OverridableDictionary which takes an underlying dictionary and features virtual methods.. – Demetris Leptos Jun 04 '17 at 09:12
  • 1
    @DemetrisLeptos. Another way of achieving that would be to explicitly implement the IDictionary interface in the MultiValueDictionary class. You can then choose which members of the interface you'd like the base class to handle, and which ones you'd like to implement yourself (in this case "Add"). – Doctor Jones Jun 05 '17 at 10:46
  • @DoctorJones yes, my "OverridableDictionary implements IDictionary" using a wrapped instance - but my focus was on that "public class MultiValueDictionary : Dictionary>" won't allow getting the Add behavior defined there when passed as an interface to e.g. a method (library etc..) using the IDictionary interface which is not aware of the MultiValueDictionary implementation – Demetris Leptos Jun 05 '17 at 16:03
3

Just to add my $0.02 to the collection of solutions:

I had the same need back in 2011 and created a MultiDictionary with a pedantically complete implementation of all the .NET interfaces. That includes enumerators that return a standard KeyValuePair<K, T> and support for the IDictionary<K, T>.Values property providing a collection of actual values (instead of an ICollection<ICollection<T>>).

That way, it fits in neatly with the rest of the .NET collection classes. I also defined an IMultiDictionary<K, T> interface to access operations that are particular to this kind of dictionary:

public interface IMultiDictionary<TKey, TValue> :
  IDictionary<TKey, ICollection<TValue>>,
  IDictionary,
  ICollection<KeyValuePair<TKey, TValue>>,
  IEnumerable<KeyValuePair<TKey, TValue>>,
  IEnumerable {

  /// <summary>Adds a value into the dictionary</summary>
  /// <param name="key">Key the value will be stored under</param>
  /// <param name="value">Value that will be stored under the key</param>
  void Add(TKey key, TValue value);

  /// <summary>Determines the number of values stored under a key</summary>
  /// <param name="key">Key whose values will be counted</param>
  /// <returns>The number of values stored under the specified key</returns>
  int CountValues(TKey key);

  /// <summary>
  ///   Removes the item with the specified key and value from the dictionary
  /// </summary>
  /// <param name="key">Key of the item that will be removed</param>
  /// <param name="value">Value of the item that will be removed</param>
  /// <returns>True if the item was found and removed</returns>
  bool Remove(TKey key, TValue value);

  /// <summary>Removes all items of a key from the dictionary</summary>
  /// <param name="key">Key of the items that will be removed</param>
  /// <returns>The number of items that have been removed</returns>
  int RemoveKey(TKey key);

}

It can be compiled on anything from .NET 2.0 upwards and so far I've deployed it on the Xbox 360, Windows Phone 7, Linux and Unity 3D. There's also a complete unit test suite covering every single line of the code.

The code is licensed under the Common Public License (short: anything goes, but bug fixes to the library's code have to published) and can be found in my Subversion repository.

Cygon
  • 9,444
  • 8
  • 42
  • 50
2

You could use MultiDictionary class from PowerCollections.

It returns ICollection{TValue} for the key asked.

Ivan Akcheurov
  • 2,173
  • 19
  • 15
1

Yet here's my attempt using ILookup<TKey, TElement> and an internal KeyedCollection. Make sure the key property is immutable.
Cross posted here.

public class Lookup<TKey, TElement> : Collection<TElement>, ILookup<TKey, TElement>
{
  public Lookup(Func<TElement, TKey> keyForItem)
    : base((IList<TElement>)new Collection(keyForItem))
  {
  }

  new Collection Items => (Collection)base.Items;

  public IEnumerable<TElement> this[TKey key] => Items[key];
  public bool Contains(TKey key) => Items.Contains(key);
  IEnumerator<IGrouping<TKey, TElement>>
    IEnumerable<IGrouping<TKey, TElement>>.GetEnumerator() => Items.GetEnumerator();

  class Collection : KeyedCollection<TKey, Grouping>
  {
    Func<TElement, TKey> KeyForItem { get; }      
    public Collection(Func<TElement, TKey> keyForItem) => KeyForItem = keyForItem;
    protected override TKey GetKeyForItem(Grouping item) => item.Key;

    public void Add(TElement item)
    {
      var key = KeyForItem(item);
      if (Dictionary != null && Dictionary.TryGetValue(key, out var collection))
        collection.Add(item);
      else
        Add(new Grouping(key) { item });
    }

    public bool Remove(TElement item)
    {
      var key = KeyForItem(item);
      if (Dictionary != null && Dictionary.TryGetValue(key, out var collection)
        && collection.Remove(item))
      {
        if (collection.Count == 0)
          Remove(key);
        return true;
      }
      return false;
    }

  }
  class Grouping : Collection<TElement>, IGrouping<TKey, TElement>
  {
    public Grouping(TKey key) => Key = key;
    public TKey Key { get; }
  }
}
Shimmy Weitzhandler
  • 101,809
  • 122
  • 424
  • 632
0

This ought to do for now...

public class MultiValueDictionary<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>>
{
    private Dictionary<TKey, LinkedList<TValue>> _dict = new Dictionary<TKey, LinkedList<TValue>>();

    public void Add(TKey key, TValue value)
    {
        if(!_dict.ContainsKey(key)) _dict[key] = new LinkedList<TValue>();
        _dict[key].AddLast(value);
    }

    public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
    {
        foreach (var list in _dict)
            foreach (var value in list.Value)
                yield return new KeyValuePair<TKey, TValue>(list.Key, value);
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}
mpen
  • 272,448
  • 266
  • 850
  • 1,236
  • Why the (awkward) LinkedList? – H H Oct 03 '10 at 18:24
  • I agree with this and Henk's ideas. Personally I don't like the idea of inheriting from `Dictionary>` (or similar) as I think it exposes too much (calling code could add/remove from the `List` value directly, for example). – Dan Tao Oct 03 '10 at 18:27
  • @Henk: Because I thought it would be more efficient than a `List` (see discussion under your answer). @Dan: Yeah... I was going to implement `IDictionary` instead, but even that.. some of the methods don't make sense. – mpen Oct 03 '10 at 18:58
  • Yes, I saw the discussion. I thought as much, the mere presence of LinkedList can be a bit misleading. It is used rarely. – H H Oct 03 '10 at 19:16
0

Alternative to custom type can be a generic extension that adds key and value when not found:

public static V getValue<K, V>(this IDictionary<K, V> d, K key) where V : new() {
    V v; if (!d.TryGetValue(key, out v)) { v = new V(); d.Add(key, v); } return v; } 

Sample use:

var d = new Dictionary<int, LinkedList<int>>();
d.getValue(1).AddLast(2);
Slai
  • 22,144
  • 5
  • 45
  • 53