2009-07-27 9 views
5

Al momento ho un menu con elementi secondari che viene memorizzato in questa variabile dizionario:Esiste una collezione di dizionari <stringa, oggetto> che consente più chiavi?

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

Così ho aggiungere viste al es la sezione "Cliente" come questo:

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

Ma dal momento che sto usando un dizionario, che può avere solo una chiave denominata "clienti".

La mia tendenza naturale sarebbe quello di creare ora una struct personalizzato con proprietà "Sezione" e "vista", ma c'è una collezione di .NET è più adatto per questo compito, qualcosa come un "MultiKeyDictionary"?

RISPOSTA:

Grazie maciejkow, ho ampliato il suo suggerimento per ottenere esattamente quello che mi serviva:

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; 
     } 
    } 

} 

Risposta 2:

Grazie Blixt per la punta su rendimento, ecco GetAllValues con tale modifica:

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

Risposta 2 re scomposto ulteriormente:

Ecco un modo molto più succinta per fare la stessa cosa, grazie Keith:

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

Per il metodo 'GetAllValues' è possibile utilizzare la parola chiave' yield' invece di generare un nuovo elenco, che utilizza più memoria ed è meno efficiente. – Blixt

+0

Potrebbe essere tutto ciò di cui hai bisogno, ma non è un completo MultiDictionary. Che dire di 'Rimuovi'? Enumerazione dei valori? ecc. –

+1

In effetti, la soluzione corretta qui è non reinventare la ruota e utilizzare una delle librerie collegate nelle risposte, o cercarne un'altra che faccia di più ciò di cui si ha bisogno. Per un tale scopo generale, ne troverai sicuramente uno ben testato e ottimizzato. – Blixt

risposta

10

Se è necessario un numero variabile di valori per una chiave, perché non creare Dictionary<string, List<UserControl>>? Inoltre, puoi ereditare questa classe e creare il tuo Aggiungi, ottieni la stessa sintassi che stai usando ora.In questo modo è possibile evitare l'aggiunta manuale di liste vuote prima di aggiungere un nuovo controllo.

qc come questo:

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

C'è un debug di riferimento a http://stackoverflow.com/questions/1187219/is-there-a-dictionarystring-object-collection-which-allows-multiple-keys/1187252#1187252? –

1

No, non c'è niente di meglio insieme incorporato. Penso che la tua "tendenza naturale" sia perfettamente adatta a risolvere questo problema, in quanto quelle non sono realmente "chiavi uguali", ma chiavi univoche composte da parti diverse e Dictionary fa il lavoro. È inoltre possibile dizionario nido (ha senso se avete un grande numero di valori per ogni nome):

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

Questo approccio risolverà all'elemento utilizzando una singola ricerca tabella di hash. Se mantieni un elenco di elementi per ciascun elemento, dovrai attraversare linearmente l'elenco ogni volta che ti serve un elemento da cercare, che sconfigge lo scopo di usare uno Dictionary in primo luogo.

0

Non conosco un "MultiKeyDictionary". Ti consigliamo di utilizzare una struct e sovrascrivere GetHashCode, Equals e implementare IEquatable < StructName> (utilizzato dal dizionario < TKey, TValue>).

4

Che ne dite di fare digitare il valore del contenitore un elenco:

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

Partenza NGenerics 'HashList. È un dizionario che mantiene un elenco di valori per ogni chiave. La libreria Wintellect's PowerCollections ha anche una comoda classe MultiDictionary che fa cose come la pulizia automatica quando si rimuove l'ultimo valore associato a una determinata chiave.

+0

+1: un wrapper che consente un modo più naturale di fare ciò che ho proposto. – Blixt

0

Stai cercando di memorizzare più voci per tasto insieme? Qualcosa come this?

3

A pochi ritocchi ...

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 per 'yield', anche se continuo a credere che sia un tempo speso male implementare il proprio wrapper quando ce ne sono quasi sicuramente molti completi (cioè hanno tutte le funzionalità che ci si aspetterebbe da un' MultiValueDictionary') e che sono stati ben- testato da molti utenti. – Blixt

+0

Può semplificare GetAllValues ​​() a questo: 'public IEnumerable GetAllValues ​​() { return this.SelectMany (keyValPair => keyValPair.Value); } ' – Kaboo

1

.NET Framework 3.5 include una classe speciale LINQ Lookup.

È simile a un dizionario tranne che può gestire più elementi con la stessa chiave. Quando esegui una ricerca utilizzando una determinata chiave, invece di ricevere un singolo elemento, ricevi un gruppo di elementi che corrispondono a quella chiave.

Ho letto che è una tabella sotto i coperchi, quindi è veloce per il recupero.

lo si utilizza qualcosa di simile:

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

Ci sono un sacco di avvertimenti:

  • La classe di ricerca non ha alcun costruttore pubblico, quindi potete semplicemente creare un oggetto di ricerca, sembra essere disponibili solo usando la sintassi .ToLookup.
  • Non puoi modificare una volta che si è creato, senza aggiungere o rimuovere ecc
  • A quanto pare la sua non serializzabile
  • Utilizzando i dati raggruppati può essere un po 'complicato

Theres un great article here discutere la Ricerca e le sue implicazioni in maggior dettaglio.

Problemi correlati