C#字典 - 一个键,多个值

31 浏览
0 Comments

C#字典 - 一个键,多个值

我想创建一个数据存储来保存一些数据。

最初的想法是创建一个字典,其中有一个键对应多个值,类似于一对多的关系。

我认为这个字典只有一个键值。

还有其他什么方式可以存储这些信息呢?

0
0 Comments

在.NET 3.5+中,使用Dictionary<IKey, List<IValue>>的代替方法是使用来自LINQ命名空间的Lookup

// 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<TKey, TElement>类似于Dictionary<TKey, TValue>。不同之处在于,Dictionary<TKey, TValue>将键映射到单个值,而Lookup<TKey, TElement>将键映射到值的集合。

可以通过在实现IEnumerable的对象上调用ToLookup来创建Lookup<TKey, TElement>的实例。

你也可以阅读这个答案来了解相关问题的更多信息。有关更多信息,请查阅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;
            }
        }
    }
}

关于不可变性的说明

默认情况下,lookups是不可变的,访问internal需要使用反射。

如果需要可变性并且不想编写自己的包装器,可以使用MultiValueDictionary(以前称为MultiDictionary)来实现(已经不再更新的Microsoft.Experimental.Collections的一部分)。

假设键和值都是字符串类型,那么为什么不使用Dictionary<string, HashSet<string>>而不是Dictionary<string, List<string>>?列表无法保证给定键的值集合中的唯一性,但是集合可以。

这不是我的回答的重点。如果您想要在值中强制执行唯一性,只需在创建查找之前使用.Distinct()。如果需要多重集合或保持项目排序或索引,列表似乎是合理的选择。

我同意您的方法的正确性和可行性。使用列表的方法中有一个隐含的假设,即每个键的值集合很小,根据这个假设,您的答案似乎是合适的。然而,从可读性的角度来看,如果我使用一些现成的东西来保持集合中的唯一性(集合),那么我认为它更可读,因为接口/API很清晰,甚至HashSet类给我的代码读者提供了一个非常清楚的信号(他们不必进入我的实现来看我在做什么)。

您是指完整示例中的第一个列表,对吗?这只是示例中的样板代码,用于展示使用ToLookupILookup与基于Dictionary的实现之间的区别。

0
0 Comments

在这段内容中,出现了一个问题:如何在C#中创建一个字典,其中一个键对应多个值。为了解决这个问题,Microsoft添加了一个名为MultiDictionary的官方预发布版本,可以通过NuGet进行下载。使用和更多细节可以在MSDN的官方博客文章中找到。

更新后,MultiValueDictionary现在在corefxlab repo中,并且可以从MyGet feed获取NuGet包。虽然博客已经三年没有更新了,但是我还没有使用MultiValueDictionary,但它实现了IReadOnlyDictionary,这是不可变的。关于它是否安全使用,是否有任何想法吗?

第二个链接已经失效。过时的内容应该被移除,而不是添加"更新"的部分。请修改你的回答,让它看起来像是今天写的。

下面是整理后的文章:

Microsoft最近添加了一个名为MultiDictionary的官方预发布版本,用于解决在C#中创建一个键对应多个值的字典的问题。你可以通过NuGet来获取这个版本。更多的使用信息和细节可以在Microsoft官方的MSDN博客文章中找到。

这个MultiValueDictionary现在已经移动到了corefxlab repo中,并且可以从MyGet feed获取NuGet包。虽然这个博客已经三年没有更新了,但是MultiValueDictionary实现了IReadOnlyDictionary接口,是不可变的。关于它是否安全使用,是否有任何想法吗?

第二个链接已经失效。过时的内容应该被移除,而不是添加"更新"的部分。请修改你的回答,让它看起来像是今天写的。

0
0 Comments

在C#中,字典(Dictionary)是一种常用的数据结构,用于存储键值对。然而,有时候我们需要在一个字典中存储多个值,而不是只有一个值。本文将讨论在C#中如何实现一个字典中的一个键对应多个值的情况以及解决方法。

问题的原因是字典(Dictionary)在默认情况下只能存储一个键对应一个值。然而,有时候我们需要在一个键对应多个值的情况下使用字典。例如,我们希望存储一个城市对应多个居民的情况,这时候一个键只对应一个值的字典就无法满足需求了。

为了解决这个问题,我们可以使用C#中的泛型类型List来存储多个值。具体而言,我们可以创建一个字典,其中键的类型是string,值的类型是List。这样,我们就可以在一个键对应多个值的情况下使用字典了。

以下是一个示例代码,展示了如何创建一个键对应多个值的字典:

Dictionary> myDict;

在上面的代码中,myDict是一个字典,其中的键是string类型,值是List类型。通过这样的数据结构,我们可以将一个键与多个值关联起来。

使用这种方式,我们可以很方便地向字典中添加多个值。例如,我们可以使用Add方法将一个键与多个值关联起来:

myDict.Add("City1", new List{"Resident1", "Resident2", "Resident3"});

上述代码将"City1"这个键与三个居民关联起来。我们还可以使用索引器来访问字典中的值:

List residents = myDict["City1"];

通过上述代码,我们可以获取与"City1"这个键关联的所有居民。

总结而言,通过在C#中使用泛型类型List,我们可以实现一个字典中的一个键对应多个值的需求。这种方式非常灵活,并且可以满足各种多值关联的需求。通过以上的示例代码和解决方法,我们可以方便地处理一个键对应多个值的情况。

0