Diccionario de valor múltiple?

¿Alguien sabe de una buena implementación de un MultiValueDictionary ? Básicamente, quiero algo que permita múltiples valores por clave. Quiero ser capaz de hacer algo como

 dict.Add(key, val); 

Y si la clave aún no existe, se agregará, si lo hace, agregará otro valor a esa clave. Voy a iterar sobre eso, así que realmente no me importan los otros métodos de recuperación.

No existe, pero puede comstackr uno bastante rápido de Dictionary and List:

 class MultiDict // no (collection) base class { private Dictionary> _data = new Dictionary>(); 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() { v}) ; } // more members } 

Microsoft acaba de agregar una versión oficial de prerescripción de exactamente lo que está buscando (llamado MultiDictionary) disponible a través de NuGet aquí: https://www.nuget.org/packages/Microsoft.Experimental.Collections/

Puede encontrar información sobre el uso y más detalles a través de la publicación oficial del blog de MSDN aquí: http://blogs.msdn.com/b/dotnet/archive/2014/06/20/would-you-like-a-multidictionary.aspx

Soy el desarrollador de este paquete, así que avíseme aquí o en MSDN si tiene alguna pregunta sobre el rendimiento o algo.

Espero que ayude.

Puedes hacer uno fácilmente desde un diccionario de listas:

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

Siempre puede usar un Tuple para su segundo parámetro genérico:

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

O incluso, una lista genérica como un segundo parámetro genérico:

 var dict = new Dictionary>(); 

Eso le permitirá vincular múltiples valores a una sola clave.

Para facilitar el uso, puede crear un método de extensión que verifique la existencia de una clave y la adición a la lista.

Aquí hay uno que escribí hace un tiempo que puedes usar.

Tiene una clase “MultiValueDictionary” que hereda de Dictionary.

También tiene una clase de extensión que le permite usar la funcionalidad especial Agregar en cualquier diccionario donde el tipo de valor es un IList; de esa manera no estás obligado a usar la clase personalizada si no quieres.

 public class MultiValueDictionary : Dictionary> { ///  /// Hide the regular Dictionary Add method ///  new private void Add(KeyType key, List value) { base.Add(key, value); } ///  /// Adds the specified value to the multi value dictionary. ///  /// The key of the element to add. /// The value of the element to add. The value can be null for reference types. 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 { ///  /// Adds the specified value to the multi value dictionary. ///  /// The key of the element to add. /// The value of the element to add. The value can be null for reference types. public static void Add(this Dictionary thisDictionary, KeyType key, ValueType value) where ListType : IList, 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); } } 

Podría usar la clase MultiDictionary de PowerCollections .

Devuelve ICollection {TValue} para la clave solicitada.

Solo para agregar mi $ 0.02 a la colección de soluciones:

Tuve la misma necesidad en 2011 y creé un MultiDictionary con una implementación pedante y completa de todas las interfaces .NET. Eso incluye enumeradores que devuelven un KeyValuePair estándar KeyValuePair y soporte para la IDictionary.Values proporciona una colección de valores reales (en lugar de un ICollection> ).

De esta manera, encaja perfectamente con el rest de las clases de colección .NET. También IMultiDictionary una IMultiDictionary para acceder a operaciones que son particulares de este tipo de diccionario:

 public interface IMultiDictionary : IDictionary>, IDictionary, ICollection>, IEnumerable>, IEnumerable { /// Adds a value into the dictionary /// Key the value will be stored under /// Value that will be stored under the key void Add(TKey key, TValue value); /// Determines the number of values stored under a key /// Key whose values will be counted /// The number of values stored under the specified key int CountValues(TKey key); ///  /// Removes the item with the specified key and value from the dictionary ///  /// Key of the item that will be removed /// Value of the item that will be removed /// True if the item was found and removed bool Remove(TKey key, TValue value); /// Removes all items of a key from the dictionary /// Key of the items that will be removed /// The number of items that have been removed int RemoveKey(TKey key); } 

Se puede comstackr en cualquier cosa desde .NET 2.0 en adelante y hasta ahora lo he implementado en Xbox 360, Windows Phone 7, Linux y Unity 3D. También hay un conjunto de pruebas de unidad completo que cubre cada línea del código.

El código está licenciado bajo la Licencia pública común (abreviado: todo vale, pero las correcciones de errores del código de la biblioteca tienen que publicarse) y se puede encontrar en mi repository de Subversion .

Sin embargo, aquí está mi bash de utilizar ILookup y un KeyedCollection interno. Asegúrese de que la propiedad clave sea inmutable.
Cruz publicada aquí .

 public class Lookup : Collection, ILookup { public Lookup(Func keyForItem) : base((IList)new Collection(keyForItem)) { } new Collection Items => (Collection)base.Items; public IEnumerable this[TKey key] => Items[key]; public bool Contains(TKey key) => Items.Contains(key); IEnumerator> IEnumerable>.GetEnumerator() => Items.GetEnumerator(); class Collection : KeyedCollection { Func KeyForItem { get; } public Collection(Func 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, IGrouping { public Grouping(TKey key) => Key = key; public TKey Key { get; } } } 

Esto debería hacer por ahora …

 public class MultiValueDictionary : IEnumerable> { private Dictionary> _dict = new Dictionary>(); public void Add(TKey key, TValue value) { if(!_dict.ContainsKey(key)) _dict[key] = new LinkedList(); _dict[key].AddLast(value); } public IEnumerator> GetEnumerator() { foreach (var list in _dict) foreach (var value in list.Value) yield return new KeyValuePair(list.Key, value); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } 

La alternativa al tipo personalizado puede ser una extensión genérica que agrega clave y valor cuando no se encuentra:

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

Uso de muestra:

 var d = new Dictionary>(); d.getValue(1).AddLast(2);