c#dictionary一键多个值

时间:2010-01-20 11:51:15

标签: c# .net dictionary datasource

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

第一个想法是创建一个字典,其中有1个具有许多值的键,所以有点像一对多的关系。

我认为字典只有1个键值。

我还能如何存储这些信息?

14 个答案:

答案 0 :(得分:67)

从.net3.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

  

查找类似于词典。该   区别在于Dictionary将键映射到单个   值,而Lookup将键映射到集合   值。

     

您可以通过调用创建Lookup实例   ToLookup实现IEnumerable的对象。

您可能还想阅读this answerrelated question。有关详细信息,请参阅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) 来自corefxlab(以前是Microsoft.Experimental.Collections的一部分,不再更新)。

答案 1 :(得分:50)

您可以使用第二种泛型类型的列表。例如,由字符串键入的字符串字典:

Dictionary<string, List<string>> myDict;

答案 2 :(得分:16)

微软刚刚通过NuGet添加了一个官方预发布版本,正是您正在寻找的(称为MultiDictionary):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,您可以从this MyGet Feed获取NuGet包。

答案 3 :(得分:7)

您的词典的值类型可以是List,也可以是包含多个对象的其他类。像

这样的东西
Dictionary<int, List<string>> 

表示由ints键入的字典并保存字符串列表。

选择值类型的主要考虑因素是您将使用Dictionary的内容,如果您必须对值进行搜索或其他操作,那么可以考虑使用可帮助您执行操作的数据结构你想要 - 就像一个HashSet。

答案 4 :(得分:7)

使用此:

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

答案 5 :(得分:5)

您可以使用Dictionary<TKey, List<TValue>>

这将允许每个键引用值列表

答案 6 :(得分:4)

使用列表字典(或其他类型的集合),例如:

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

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

答案 7 :(得分:1)

您可以将包含集合(或任何其他类型/类)的字典作为值。这样,您只需一个密钥,就可以将值存储在集合中。

答案 8 :(得分:1)

.NET字典与键和值只有一对一的关系。但这并不意味着值不能是另一个数组/列表/字典。

我想不出在词典中有一对多关系的理由,但显然有一个。

如果您要将某种类型的数据存储到某个键,那么这听起来就像是创建自己的类的理想时间。然后你有一个1比1,但你有值类存储更多的1个数据。

答案 9 :(得分:1)

查看Microsoft的MultiValueDictionary

示例代码:

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

答案 10 :(得分:0)

这是实现此行为的方法。

要获得涉及ILookup<TKey, TElement>的更全面的解决方案,请查看my other answer

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

注意:密钥必须是不可变的(或者在密钥更改时删除并重新添加)。

答案 11 :(得分:0)

您也可以使用;

 List<KeyValuePair<string, string>> Mappings;

答案 12 :(得分:0)

你可以创建一个非常简单的多字典,它可以自动处理插入这样的值:

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方法的重载版本。如果此条目的条目尚未存在,则原始密钥允许您插入密钥的项目列表。此版本允许您在任何情况下插入单个项目。

答案 13 :(得分:0)

 Dictionary<int, string[]> dictionaty  = new Dictionary<int, string[]>() {
            {1, new string[]{"a","b","c"} },
            {2, new string[]{"222","str"} }
        };