2009-07-27 12 views
5

Actualmente tengo un menú con elementos secundarios que se almacena en esta variable diccionario:¿Hay una colección Dictionary <string, object> que permita varias claves?

private Dictionary<string, UserControl> _leftSubMenuItems 
    = new Dictionary<string, UserControl>(); 

Así que añadir vistas a la p la sección "Cliente" de esta manera:

_leftSubMenuItems.Add("customers", container.Resolve<EditCustomer>()); 
_leftSubMenuItems.Add("customers", container.Resolve<CustomerReports>()); 

Pero ya que estoy usando un diccionario, que sólo puede tener una clave llamada "clientes".

Mi tendencia natural sería crear ahora una estructura personalizada con propiedades "sección" y "Ver", pero hay una colección de .NET es más adecuado para esta tarea, algo así como un "MultiKeyDictionary"?

RESPUESTA:

Gracias maciejkow, que amplió su sugerencia para conseguir exactamente lo que necesitaba:

using System; 
using System.Collections.Generic; 

namespace TestMultiValueDictionary 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      MultiValueDictionary<string, object> leftSubMenuItems = new MultiValueDictionary<string, object>(); 

      leftSubMenuItems.Add("customers", "customers-view1"); 
      leftSubMenuItems.Add("customers", "customers-view2"); 
      leftSubMenuItems.Add("customers", "customers-view3"); 
      leftSubMenuItems.Add("employees", "employees-view1"); 
      leftSubMenuItems.Add("employees", "employees-view2"); 

      foreach (var leftSubMenuItem in leftSubMenuItems.GetValues("customers")) 
      { 
       Console.WriteLine(leftSubMenuItem); 
      } 

      Console.WriteLine("---"); 

      foreach (var leftSubMenuItem in leftSubMenuItems.GetAllValues()) 
      { 
       Console.WriteLine(leftSubMenuItem); 
      } 

      Console.ReadLine(); 
     } 
    } 

    public class MultiValueDictionary<TKey, TValue> : Dictionary<TKey, List<TValue>> 
    { 

     public void Add(TKey key, TValue value) 
     { 
      if (!ContainsKey(key)) 
       Add(key, new List<TValue>()); 
      this[key].Add(value); 
     } 

     public List<TValue> GetValues(TKey key) 
     { 
      return this[key]; 
     } 

     public List<TValue> GetAllValues() 
     { 
      List<TValue> list = new List<TValue>(); 

      foreach (TKey key in this.Keys) 
      { 
       List<TValue> values = this.GetValues(key); 
       list.AddRange(values); 
      } 

      return list; 
     } 
    } 

} 

Respuesta 2:

Gracias Blixt por la punta sobre el rendimiento, aquí es GetAllValues con ese cambio:

public IEnumerable<TValue> GetAllValues() 
{ 
    foreach (TKey key in this.Keys) 
    { 
     List<TValue> values = this.GetValuesForKey(key); 
     foreach (var value in values) 
     { 
      yield return value; 
     } 
    } 
} 

Responder 2 re factorizar más:

Aquí es una manera mucho más sucinta a hacer lo mismo, gracias Keith:

public IEnumerable<TValue> GetAllValues() 
{ 
    foreach (var keyValPair in this) 
     foreach (var val in keyValPair.Value) 
      yield return val; 
} 
+1

Para su método 'GetAllValues' puede usar la palabra clave' yield' en lugar de generar una nueva lista, que usa más memoria y es menos eficiente. – Blixt

+0

Eso podría ser todo lo que necesita, pero no es una MultiDictionary completa. ¿Qué pasa con 'Eliminar'? ¿Enumerando valores? etc. –

+1

De hecho, la solución adecuada aquí es no reinventar la rueda y usar una de las bibliotecas vinculadas en las respuestas, o buscar otra que haga más lo que necesita. Para un propósito tan general, seguramente encontrará uno que esté bien probado y optimizado. – Blixt

Respuesta

10

Si necesita una cantidad variable de valores para una clave, ¿por qué no crear Dictionary<string, List<UserControl>>? Además, puedes heredar esta clase y crear tu propio Add, obtener la misma sintaxis que estás usando ahora.De esta forma, puede evitar la adición manual de listas vacías antes de agregar un nuevo control.

algo como esto:

class MultiValueDictionary<TKey, TValue> : Dictionary<TKey, List<TValue>> 
{ 

    public void Add(TKey key, TValue value) 
    { 
     if(!ContainsKey(key)) 
     Add(key, new List<TValue>()); 
     this[key].Add(value); 
    } 
} 
+0

Hay uno depurado al que se hace referencia en http://stackoverflow.com/questions/1187219/is-there-a-dictionarystring-object-collection-which-allows-multiple-keys/1187252#1187252? –

1

No, no hay mejor colección integrada. Creo que su "tendencia natural" es perfecta para resolver este problema, ya que esas no son realmente las "mismas teclas", sino que las claves únicas compuestas por diferentes partes y Dictionary hacen el trabajo. También puede diccionario nido (que tiene sentido si tiene gran número de valores para cada nombre):

Dictionary<string, Dictionary<Type, object>> dict = ...; 
var value = (T)dict[name][typeof(T)]; 

Este enfoque se resolverá al elemento utilizando una sola búsqueda en la tabla hash. Si mantiene una lista de elementos para cada elemento, tendrá que atravesar linealmente la lista cada vez que necesite un elemento para buscar que, en primer lugar, anula el objetivo de usar un Dictionary.

0

No conozco un "MultiKeyDictionary". Recomiendo usar una estructura y reemplazar GetHashCode, Equals e implementar IEquatable < StructName> (que es usado por Dictionary < TKey, TValue>).

4

¿Qué tal hacer el valor Tipo de contenedor una lista:

private Dictionary<string, List<UserControl>> _leftSubMenuItems = 
    new Dictionary<string, List<UserControl>>(); 

if (!_leftSubMenuItems.ContainsKey("customers")) 
{ 
    _leftSubMenuItems["customers"] = new List<UserControl>(); 
} 
_leftSubMenuItems["customers"].Add(container.Resolve<EditCustomer>()); 
_leftSubMenuItems["customers"].Add(container.Resolve<CustomerReports>()); 
5

Salida NGenerics 'HashList. Es un diccionario que mantiene una lista de valores para cada clave. La biblioteca Wintellect's PowerCollections también tiene una práctica clase MultiDictionary que hace cosas como limpiar automáticamente cuando elimina el último valor asociado con una tecla determinada.

+0

+1: Un contenedor que permite una forma más natural de hacer lo que propongo. – Blixt

0

¿Está buscando para almacenar múltiples entradas por clave juntos? Somethign como this?

3

sólo algunos retoques ...

public class MultiValueDictionary<TKey, TValue> : 
    Dictionary<TKey, List<TValue>> 
{ 

    public void Add(TKey key, TValue value) 
    { 
     List<TValue> valList; 
     //a single TryGetValue is quicker than Contains then [] 
     if (this.TryGetValue(key, out valList)) 
      valList.Add(value); 
     else 
      this.Add(key, new List<TValue> { value }); 
    } 

    //this can be simplified using yield 
    public IEnumerable<TValue> GetAllValues() 
    { 
     //dictionaries are already IEnumerable, you don't need the extra lookup 
     foreach (var keyValPair in this) 
      foreach(var val in keyValPair.Value); 
       yield return val; 
    } 
} 
+0

+1 por 'rendimiento', aunque sigo creyendo que ha pasado mucho tiempo implementando su propio contenedor cuando casi seguro hay varios más que están completos (es decir, tienen toda la funcionalidad que cabría esperar de un' MultiValueDictionary') y que han sido bien- probado por muchos usuarios. – Blixt

+0

Puede simplificar GetAllValues ​​() a esto: 'public IEnumerable GetAllValues ​​() { return this.SelectMany (keyValPair => keyValPair.Value); } ' – Kaboo

1

marco .NET 3.5 incluye una clase especial de LINQ Lookup.

Es similar a un diccionario, excepto que puede manejar varios elementos con la misma clave. Cuando realiza una búsqueda con una clave determinada, en lugar de recibir un elemento individual, recibe un grupo de elementos que coinciden con esa clave.

He leído que es una tabla hash debajo de las cubiertas por lo que es rápido para recuperar.

que lo utilice algo como esto:

var example1 = (from element in ListWithDuplicates 
      select element) 
      .ToLookup(A => A.Name); 

Hay un montón de advertencias:

  • La clase de búsqueda no tiene ningún constructor público, así que no puedo acaba de crear un objeto de búsqueda, parece solo estará disponible con la sintaxis .ToLookup.
  • No puede editar una vez que se ha creado, sin Agregar o quitar etc.
  • Al parecer no es serializable
  • Utilizando los datos agrupados puede ser un poco complicado

Hay una great article here discutir la búsqueda y sus implicaciones con más detalle.

Cuestiones relacionadas