c # словарь один ключ много значений - PullRequest
61 голосов
/ 20 января 2010

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

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

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

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

Ответы [ 13 ]

67 голосов
/ 07 июня 2012

Начиная с .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, который больше не обновляется).

50 голосов
/ 20 января 2010

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

Dictionary<string, List<string>> myDict;
16 голосов
/ 21 июня 2014

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 , и вы можете получить пакет NuGet из этого канала MyGet.

7 голосов
/ 04 мая 2013

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

Dictionary<TKey, Tuple<TValue1, TValue2, TValue3, ...>>
7 голосов
/ 20 января 2010

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

Dictionary<int, List<string>> 

для словаря, в котором введены целые числа и содержится список строк.

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

5 голосов
/ 20 января 2010

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

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

4 голосов
/ 20 января 2010

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

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

myDictionary["My key"] = new List<int> {1, 2, 3, 4, 5};
1 голос
/ 08 января 2018

Взгляните на 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
1 голос
/ 20 января 2010

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

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

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

1 голос
/ 20 января 2010

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

...