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?
À 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 à internal
s 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).
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;
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.
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.
Utilisez ceci:
Dictionary<TKey, Tuple<TValue1, TValue2, TValue3, ...>>
Vous pouvez utiliser un Dictionary<TKey, List<TValue>>
.
Cela permettrait à chaque clé de référencer une liste de valeurs.
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};
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.
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.
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
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é).
Vous pouvez aussi utiliser;
List<KeyValuePair<string, string>> Mappings;