web-dev-qa-db-fra.com

dictionnaire c # une clé plusieurs valeurs

Je veux créer un magasin de données pour me permettre de stocker des données.

La première idée était de créer un dictionnaire avec une clé avec plusieurs valeurs, un peu comme une relation un à plusieurs.

Je pense que le dictionnaire a seulement 1 valeur de clé.

Sinon, comment pourrais-je stocker cette information?

61
Marc G

À partir de .net3.5 + au lieu d'utiliser un Dictionary<IKey, List<IValue>>, vous pouvez utiliser une Lookup à partir de l'espace de noms 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 } _:

Une recherche ressemble à un dictionnaire. Le La différence est qu’un dictionnaire mappe les clés sur single valeurs, alors qu’une recherche mappe les clés à des collections de valeurs.

Vous pouvez créer une instance d'une recherche en appelant ToLookup sur un objet qui implémente IEnumerable.

Vous voudrez peut-être aussi lire cette réponse en question connexe . Pour plus d'informations, consultez msdn .

Exemple complet:

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

Remarque sur l'immuabilité

Par défaut, les recherches sont un peu immuables et accéder à internals impliquerait une réflexion. Si vous avez besoin de mutabilité et que vous ne voulez pas écrire votre propre wrapper, vous pouvez utiliser MultiValueDictionary (anciennement appelé MultiDictionary ) De corefxlab (anciennement partie de Microsoft.Experimental.Collections qui n'est plus mis à jour).

67
mbx

Vous pouvez utiliser une liste pour le deuxième type générique. Par exemple, un dictionnaire de chaînes indexées par une chaîne:

Dictionary<string, List<string>> myDict;
49
Oded

Microsoft vient d'ajouter une version préliminaire préliminaire de ce que vous recherchez (appelée MultiDictionary) disponible via NuGet ici: https://www.nuget.org/packages/Microsoft.Experimental.Collections/

Des informations sur l'utilisation et plus de détails sont disponibles sur le blog officiel MSDN, à l'adresse suivante: http://blogs.msdn.com/b/dotnet/archive/2014/06/20/would-you-like-a-multidictionary .aspx

Je suis le développeur de ce paquet, alors faites-le moi savoir ici ou sur MSDN si vous avez des questions sur les performances ou sur quoi que ce soit.

J'espère que cela pourra aider.

Mettre à jour

La MultiValueDictionary est maintenant sur le répertoire corefxlab , et vous pouvez obtenir le package NuGet à partir de this MyGet.

16
Ian Hays

Le type de valeur de votre dictionnaire peut être une liste ou une autre classe contenant plusieurs objets. Quelque chose comme 

Dictionary<int, List<string>> 

pour un dictionnaire qui est saisi par ints et contient une liste de chaînes.

Le choix du type de valeur dépend principalement de l'utilisation du dictionnaire. Si vous devez effectuer des recherches ou effectuer d'autres opérations sur les valeurs, pensez peut-être à utiliser une structure de données qui vous aidera à faire ce que vous voulez - - comme un HashSet.

7
Tim Ridgely

Utilisez ceci:

Dictionary<TKey, Tuple<TValue1, TValue2, TValue3, ...>>
7
Gisway

Vous pouvez utiliser un Dictionary<TKey, List<TValue>>.

Cela permettrait à chaque clé de référencer une liste de valeurs.

5
Blorgbeard

Utilisez un dictionnaire de listes (ou un autre type de collection), par exemple:

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

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

Vous pouvez avoir un dictionnaire avec une collection (ou tout autre type/classe) comme valeur. De cette façon, vous avez une clé unique et vous stockez les valeurs dans votre collection.

1
Maurits Rijk

Un dictionnaire .NET a uniquement une relation de 1 à 1 pour les clés et les valeurs. Mais cela ne signifie pas qu'une valeur ne peut pas être un autre tableau/liste/dictionnaire.

Je ne vois pas de raison d'avoir une relation 1 à plusieurs dans un dictionnaire, mais il y en a évidemment une.

Si vous souhaitez stocker différents types de données sur une clé, cela semble être le moment idéal pour créer votre propre classe. Ensuite, vous avez un 1 pour 1, mais vous avez la classe de valeur stockant plus que 1 donnée.

1
Alastair Pitts

Jetez un coup d’œil à MultiValueDictionary de Microsoft.

Exemple de code: 

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
1
Ali Asad

Vous pouvez créer un multi-dictionnaire très simpliste, qui automatise le traitement de l'insertion de valeurs telles que

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

Cela crée une version surchargée de la méthode Add. L'original vous permet d'insérer une liste d'éléments pour une clé, si aucune entrée pour cette entrée n'existe encore. Cette version vous permet d'insérer un seul élément dans tous les cas.

Voici mon approche pour obtenir ce comportement.

Pour une solution plus complète impliquant ILookup<TKey, TElement>, consultez mon autre réponse .

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

Remarque: la clé doit être immuable (ou supprimer et rajouter au changement de clé).

0
Shimmy

Vous pouvez aussi utiliser;

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