2010-01-20 15 views
54

Voglio creare un archivio dati per consentirmi di memorizzare alcuni dati.C# dizionario una chiave molti valori

La prima idea era quella di creare un dizionario in cui si dispone di 1 chiave con molti valori, in modo un po 'come un uno a molti.

penso che il dizionario ha solo 1 valore della chiave.

In quale altro modo è possibile memorizzare queste informazioni?

risposta

45

È possibile utilizzare un elenco per il secondo tipo generico. Ad esempio, un dizionario di stringhe calettato da una stringa:

Dictionary<string, List<string>> myDict; 
5

si potrebbe usare un Dictionary<TKey, List<TValue>>.

Ciò consentirebbe a ciascuna chiave di fare riferimento a un elenco di valori .

1

si può avere un dizionario con una collezione (o qualsiasi altro tipo/classe) come valore. In questo modo hai una sola chiave e memorizzi i valori nella tua collezione.

1

dizionario A. NET non hanno solo un rapporto di 1 a 1 per le chiavi e valori. Ma ciò non significa che un valore non può essere un altro array/elenco/dizionario.

non riesco a pensare a una ragione per avere un rapporto di 1 a molti in un dizionario, ma ovviamente c'è uno.

Se si dispone di diversi tipi di dati che si desidera memorizzare su un tasto, poi che suona come il momento ideale per creare la propria classe. Quindi hai un 1 a 1, ma hai la classe value che memorizza più di 1 pezzo di dati.

6

del vostro dizionario tipo di valore potrebbe essere un elenco, o di altra classe che contiene più oggetti. Qualcosa come

Dictionary<int, List<string>> 

per un dizionario che è codificato da ints e contiene un elenco di stringhe.

Una considerazione importante nella scelta del tipo di valore è ciò che utilizzerai per il dizionario, se dovrai eseguire ricerche o altre operazioni sui valori, quindi potresti pensare di utilizzare una struttura dati che ti aiuti a fare quello che vuoi - come un HashSet.

4

utilizzare un dizionario di liste (o un altro tipo di raccolta), ad esempio:

var myDictionary = new Dictionary<string, IList<int>>(); 

myDictionary["My key"] = new List<int> {1, 2, 3, 4, 5}; 
61

A partire dal .net3.5 + invece di utilizzare un Dictionary<IKey, List<IValue>> è possibile utilizzare un Lookup dallo spazio dei nomi Linq:

// lookup Order by payment status (1:m) 
// would need something like Dictionary<Boolean, IEnumerable<Order>> orderIdByIsPayed 
ILookup<Boolean, Order> byPayment = orderList.ToLookup(o => o.IsPayed); 
IEnumerable<Order> payedOrders = byPayment[false]; 

Da msdn:

una ricerca assomiglia a un dizionario. La differenza è che un dizionario associa le chiavi ai singoli valori , mentre una Ricerca esegue il mapping delle chiavi alle raccolte di valori .

È possibile creare un'istanza di una ricerca chiamando ToLookup su un oggetto che implementa IEnumerable.

Si consiglia inoltre di leggere this answer a related question. Per maggiori informazioni, consultare msdn.

esempio completa:

using System; 
using System.Collections.Generic; 
using System.Linq; 

namespace LinqLookupSpike 
{ 
    class Program 
    { 
     static void Main(String[] args) 
     { 
      // init 
      var orderList = new List<Order>(); 
      orderList.Add(new Order(1, 1, 2010, true));//(orderId, customerId, year, isPayed) 
      orderList.Add(new Order(2, 2, 2010, true)); 
      orderList.Add(new Order(3, 1, 2010, true)); 
      orderList.Add(new Order(4, 2, 2011, true)); 
      orderList.Add(new Order(5, 2, 2011, false)); 
      orderList.Add(new Order(6, 1, 2011, true)); 
      orderList.Add(new Order(7, 3, 2012, false)); 

      // lookup Order by its id (1:1, so usual dictionary is ok) 
      Dictionary<Int32, Order> orders = orderList.ToDictionary(o => o.OrderId, o => o); 

      // lookup Order by customer (1:n) 
      // would need something like Dictionary<Int32, IEnumerable<Order>> orderIdByCustomer 
      ILookup<Int32, Order> byCustomerId = orderList.ToLookup(o => o.CustomerId); 
      foreach (var customerOrders in byCustomerId) 
      { 
       Console.WriteLine("Customer {0} ordered:", customerOrders.Key); 
       foreach (var order in customerOrders) 
       { 
        Console.WriteLine(" Order {0} is payed: {1}", order.OrderId, order.IsPayed); 
       } 
      } 

      // the same using old fashioned Dictionary 
      Dictionary<Int32, List<Order>> orderIdByCustomer; 
      orderIdByCustomer = byCustomerId.ToDictionary(g => g.Key, g => g.ToList()); 
      foreach (var customerOrders in orderIdByCustomer) 
      { 
       Console.WriteLine("Customer {0} ordered:", customerOrders.Key); 
       foreach (var order in customerOrders.Value) 
       { 
        Console.WriteLine(" Order {0} is payed: {1}", order.OrderId, order.IsPayed); 
       } 
      } 

      // lookup Order by payment status (1:m) 
      // would need something like Dictionary<Boolean, IEnumerable<Order>> orderIdByIsPayed 
      ILookup<Boolean, Order> byPayment = orderList.ToLookup(o => o.IsPayed); 
      IEnumerable<Order> payedOrders = byPayment[false]; 
      foreach (var payedOrder in payedOrders) 
      { 
       Console.WriteLine("Order {0} from Customer {1} is not payed.", payedOrder.OrderId, payedOrder.CustomerId); 
      } 
     } 

     class Order 
     { 
      // key properties 
      public Int32 OrderId { get; private set; } 
      public Int32 CustomerId { get; private set; } 
      public Int32 Year { get; private set; } 
      public Boolean IsPayed { get; private set; } 

      // additional properties 
      // private List<OrderItem> _items; 

      public Order(Int32 orderId, Int32 customerId, Int32 year, Boolean isPayed) 
      { 
       OrderId = orderId; 
       CustomerId = customerId; 
       Year = year; 
       IsPayed = isPayed; 
      } 
     } 
    } 
} 

Osservazioni sulla immutabilità

Per impostazione predefinita, Ricerche sono una specie di immutabili e l'accesso ai internal s comporterebbe riflessione. Se avete bisogno di mutevolezza e non si vuole scrivere il proprio involucro, si potrebbe usare MultiValueDictionary (precedentemente noto come MultiDictionary) da corefxlab (già parte del Microsoft.Experimental.Collections che non viene più aggiornato).

+0

Ricerche sono immutabili, si prega di segnalare questo nella vostra risposta. – Shimmy

+0

Controlla [risposta] (https://stackoverflow.com/a/45824919/75500). – Shimmy

+2

@Shimmy aggiornato come richiesto – mbx

7

Utilizzare questa:

Dictionary<TKey, Tuple<TValue1, TValue2, TValue3, ...>> 
16

Microsoft appena aggiunto una versione ufficiale di prelease esattamente quello che stai cercando (chiamato MultiDictionary) disponibili tramite NuGet qui: https://www.nuget.org/packages/Microsoft.Experimental.Collections/

Informazioni su uso e ulteriori dettagli può essere trovato tramite il post ufficiale del blog MSDN qui: http://blogs.msdn.com/b/dotnet/archive/2014/06/20/would-you-like-a-multidictionary.aspx

Sono lo sviluppatore di questo pacchetto, quindi fammi sapere qui o su MSDN se hai domande su prestazioni o anythi ng.

Spero che questo aiuti.

Aggiornamento

Il MultiValueDictionary è ora sul corefxlab repo, e si può ottenere il pacchetto NuGet da this alimentazione MyGet.

+0

Sembra che ora si chiami MultiValueDictionary. Voglio usare questo, ma non sono sicuro del suo futuro. Il blog non è stato aggiornato tra 3 anni. Qualche idea sul fatto che sia sicuro da usare? –

+0

Non ho sperimentato con 'MultiValueDictionary', ma implementa' IReadOnlyDictionary' che è immutabile. Ad ogni modo ho aggiornato la tua risposta, sembra che quello strumento sia passato al repository corefxlab. – Shimmy

0

Ecco il mio approccio per ottenere questo comportamento.

Per una soluzione più completa che coinvolge ILookup<TKey, TElement>, controllare my other answer.

public abstract class Lookup<TKey, TElement> : KeyedCollection<TKey, ICollection<TElement>> 
{ 
    protected override TKey GetKeyForItem(ICollection<TElement> item) => 
    item 
    .Select(b => GetKeyForItem(b)) 
    .Distinct() 
    .SingleOrDefault(); 

    protected abstract TKey GetKeyForItem(TElement item); 

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

    public void Remove(TElement item) 
    { 
    var key = GetKeyForItem(item); 
    if (Dictionary != null && Dictionary.TryGetValue(key, out var collection)) 
    { 
     collection.Remove(item); 
     if (collection.Count == 0) 
     Remove(key); 
    } 
    } 
} 

Usage:

public class Item 
{ 
    public string Key { get; } 
    public string Value { get; set; } 
    public Item(string key, string value = null) { Key = key; Value = value; } 
} 

public class Lookup : Lookup<string, Item> 
{ 
    protected override string GetKeyForItem(Item item) => item.Key; 
} 

static void Main(string[] args) 
{ 
    var toRem = new Item("1", "different"); 
    var single = new Item("2", "single"); 
    var lookup = new Lookup() 
    { 
    new Item("1", "hello"), 
    new Item("1", "hello2"), 
    new Item(""), 
    new Item("", "helloo"), 
    toRem, 
    single 
    }; 

    lookup.Remove(toRem); 
    lookup.Remove(single); 
} 

Nota: la chiave deve essere immutabili (o rimuovere e aggiungere nuovamente sul tasto di cambio).

0

È inoltre possibile utilizzare;

List<KeyValuePair<string, string>> Mappings; 
0

È possibile creare una molto semplicistico multi-dizionario, che automatizza al processo di inserimento di valori in questo modo:

public class MultiDictionary<TKey, TValue> : Dictionary<TKey, List<TValue>> 
{ 
    public void Add(TKey key, TValue value) 
    { 
     if (TryGetValue(key, out List<TValue> valueList)) { 
      valueList.Add(value); 
     } else { 
      Add(key, new List<TValue> { value }); 
     } 
    } 
} 

Questo crea una versione di overload del metodo Add. Quello originale consente di inserire un elenco di elementi per una chiave, se non esiste ancora una voce per questa voce. Questa versione ti consente di inserire un singolo oggetto in ogni caso.

0

Dai un'occhiata a MultiValueDictionary da Microsoft.

Esempio di codice:

MultiValueDictionary<string, string> Parameters = new MultiValueDictionary<string, string>(); 

Parameters.Add("Malik", "Ali"); 
Parameters.Add("Malik", "Hamza"); 
Parameters.Add("Malik", "Danish"); 

//Parameters["Malik"] now contains the values Ali, Hamza, and Danish 
Problemi correlati