Подтвердить что ты не робот

С# словарь один ключ многие значения

Я хочу создать хранилище данных, чтобы я мог хранить некоторые данные.

Первой идеей было создание словаря, в котором у вас есть 1 ключ со многими значениями, поэтому немного похоже на отношение от одного до многих.

Я думаю, что словарь имеет только 1 ключевое значение.

Как еще я могу хранить эту информацию?

4b9b3361

Ответ 1

По состоянию на .net3 5+ вместо использования. Dictionary<IKey, List<IValue>> Вы можете использовать Lookup из пространства имен 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];

Из MSDN:

Lookup похож на словарь. Разница в том, что словарь отображает ключи на отдельные значения, тогда как поиск отображает ключи на коллекции значений.

Вы можете создать экземпляр Lookup, вызвав ToLookup для объекта, который реализует IEnumerable.

Вы также можете прочитать этот ответ на связанный вопрос. Для получения дополнительной информации обратитесь к MSDN.

Полный пример:

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

Замечание об неизменности

По умолчанию поиски являются неизменными, а доступ к internal объектам подразумевает отражение. Если вам нужна изменчивость и вы не хотите писать свою собственную оболочку, вы можете использовать MultiValueDictionary (ранее известный как MultiDictionary) из corefxlab (ранее часть Microsoft.Experimental.Collections которая больше не обновляется).

Ответ 2

Вы можете использовать список для второго типа. Например, словарь строк, набранных строкой:

Dictionary<string, List<string>> myDict;

Ответ 3

Microsoft просто добавила официальную предварительную версию именно того, что вы ищете (называемый MultiDictionary), доступный через NuGet здесь: https://www.nuget.org/packages/Microsoft.Experimental.Collections/

Информацию об использовании и более подробную информацию можно найти в официальном сообщении в блоге MSDN здесь: http://blogs.msdn.com/b/dotnet/archive/2014/06/20/would-you-like-a-multidictionary.aspx

Я разработчик этого пакета, поэтому дайте мне знать либо здесь, либо в MSDN, если у вас есть какие-либо вопросы о производительности или что-то еще.

Надеюсь, что это поможет.

Обновление

MultiValueDictionary теперь находится на corefxlab repo, и вы можете получить пакет NuGet из this MyGet feed.

Ответ 4

Тип словаря может быть списком или другим классом, который содержит несколько объектов. Что-то вроде

Dictionary<int, List<string>> 

для словаря, который вводится с помощью int и содержит список строк.

Основное внимание при выборе типа значения - это то, что вы будете использовать в словаре, если вам нужно будет выполнять поиск или другие операции над значениями, тогда, возможно, подумайте об использовании структуры данных, которая поможет вам сделать то, что вы хотите - как HashSet.

Ответ 5

Используйте это:

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

Ответ 6

Вы можете использовать Dictionary<TKey, List<TValue>>.

Это позволит каждому ключу ссылаться на список значений.

Ответ 7

Используйте словарь списков (или другой тип коллекции), например:

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

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

Ответ 8

В качестве значения вы можете иметь словарь с коллекцией (или любым другим типом/классом). Таким образом, у вас есть один ключ, и вы сохраняете значения в своей коллекции.

Ответ 9

У словаря .NET есть только отношение 1 к 1 для ключей и значений. Но это не означает, что значение не может быть другим массивом/списком/словарем.

Я не могу придумать, почему в словаре есть отношение от 1 до многих, но, очевидно, есть один.

Если у вас есть разные типы данных, которые вы хотите сохранить для ключа, это похоже на идеальное время для создания собственного класса. Затем у вас есть от 1 до 1, но у вас есть класс значений, хранящий более 1 часть данных.

Ответ 10

Взгляните на MultiValueDictionary от Microsoft.

Пример кода:

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

Ответ 11

Вот мой подход к достижению такого поведения.

Для более полного решения, включающего ILookup<TKey, TElement>, проверьте мой другой ответ.

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

Использование:

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

Примечание: ключ должен быть неизменным (или удалять и повторно добавлять при смене ключа).

Ответ 12

Вы также можете использовать;

 List<KeyValuePair<string, string>> Mappings;

Ответ 13

Вы можете создать очень упрощенный многозадачный словарь, который автоматизирует процесс вставки таких значений:

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

Это создает перегруженную версию метода Add. Исходный позволяет вам вставить список элементов для ключа, если для этой записи еще нет записи. Эта версия позволяет вставлять один элемент в любом случае.

Ответ 14

я использую это:

public class ListMap<T,V> : List<KeyValuePair<T, V>>
{
    public void Add(T key, V value) {
        Add(new KeyValuePair<T, V>(key, value));
    }

    public List<V> Get(T key) {
        return FindAll(p => p.Key.Equals(key)).ConvertAll(p=> p.Value);
    }
}

использование:

var fruits = new ListMap<int, string>();
fruits.Add(1, "apple");
fruits.Add(1, "orange");
var c = fruits.Get(1).Count; //c = 2;