2010-01-20 22 views
54

Quiero crear un almacén de datos que me permita almacenar algunos datos.C# diccionario una clave muchos valores

La primera idea fue crear un diccionario en el que tiene 1 clave con muchos valores, por lo que se parece a una relación de uno a muchos.

Creo que el diccionario solo tiene 1 valor de clave.

¿De qué otra forma podría almacenar esta información?

Respuesta

45

Puede usar una lista para el segundo tipo genérico. Por ejemplo, un diccionario de cadenas tecleó por una cadena:

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

Se puede usar un Dictionary<TKey, List<TValue>>.

Eso permitiría que cada tecla haga referencia a una lista de valores.

1

Puede tener un diccionario con una colección (o cualquier otro tipo/clase) como valor. De esta forma, tiene una sola clave y almacena los valores en su colección.

1

Un diccionario .NET solo tiene una relación 1-a-1 para claves y valores. Pero eso no significa que un valor no puede ser otra matriz/lista/diccionario.

No puedo pensar en una razón para tener una relación de 1 a muchos en un diccionario, pero obviamente hay uno.

Si tiene diferentes tipos de datos que desea almacenar en una clave, entonces eso suena como el momento ideal para crear su propia clase. Entonces tienes un 1 a 1, pero tienes la clase de valor que almacena más de 1 dato.

6

El tipo de valor de su diccionario podría ser una lista u otra clase que contenga varios objetos. Algo como

Dictionary<int, List<string>> 

para un diccionario que está codificado por ints y contiene una lista de cadenas.

Una consideración principal al elegir el tipo de valor es para lo que usará el Diccionario, si debe realizar búsquedas u otras operaciones en los valores, entonces quizás piense en usar una estructura de datos que lo ayude a hacer lo que quieres, como un HashSet.

4

usar un diccionario de listas (u otro tipo de colección), por ejemplo:

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

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

A partir de .net3.5 + en lugar de utilizar un Dictionary<IKey, List<IValue>> puede utilizar un Lookup del espacio de nombres de 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]; 

De msdn:

una búsqueda se asemeja a un diccionario. La diferencia es que un diccionario asigna claves a los valores individuales , mientras que una búsqueda asigna claves a las colecciones de los valores .

Puede crear una instancia de una búsqueda llamando al ToLookup en un objeto que implementa IEnumerable.

Le recomendamos que lea this answer en un related question. Para obtener más información, consulte msdn.

ejemplo completo:

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

Observación sobre la inmutabilidad

Por defecto, las búsquedas son una especie de inmutable y acceder a los internal s implicarían la reflexión. Si necesita mutabilidad y no desea escribir su propio contenedor, puede usar MultiValueDictionary (anteriormente conocido como MultiDictionary) desde corefxlab (anteriormente parte de Microsoft.Experimental.Collections que ya no se actualiza).

+0

búsquedas son inmutables, por favor señalar esto en su respuesta. – Shimmy

+0

Revisa [mi respuesta] (https://stackoverflow.com/a/45824919/75500). – Shimmy

+2

@Shimmy actualizado según lo solicitado – mbx

7

Utilice esta:

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

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

información sobre el uso y más detalles se puede encontrar a través de la publicación oficial de 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 háganmelo saber aquí o en MSDN si tiene alguna pregunta sobre el rendimiento o cualquier otra cosa ng.

Espero que ayude.

actualización

El MultiValueDictionary está ahora en el corefxlab repo, y se puede obtener el paquete de NuGet this alimentación MyGet.

+0

Parece que ahora se llama MultiValueDictionary. Quiero usar esto, pero no estoy seguro de su futuro. El blog no se ha actualizado en 3 años. ¿Alguna idea sobre si esto es seguro de usar? –

+0

No he experimentado con el 'MultiValueDictionary', pero implementa' IReadOnlyDictionary' que es inmutable. De todos modos, he actualizado tu respuesta, parece que esa herramienta se trasladó al repositorio corefxlab. – Shimmy

0

Este es mi enfoque para lograr este comportamiento.

Para obtener una solución más completa que implique ILookup<TKey, TElement>, consulte 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); 
    } 
    } 
} 

Uso:

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 clave debe ser inmutable (o quitar y volver a agregar a la clave de cambio).

0

También puede usar;

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

Puede crear un multi-diccionario muy simplista, que automatiza al proceso de inserción de los valores de la siguiente manera:

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

Esto crea una versión sobrecargada del método Add. El original le permite insertar una lista de elementos para una clave, si aún no existe una entrada para esta entrada. Esta versión le permite insertar un solo elemento en cualquier caso.

0

Eche un vistazo a MultiValueDictionary de Microsoft.

Código Ejemplo:

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 
Cuestiones relacionadas