具有多个键和每个键的多个值的字典
本文关键字:字典 | 更新日期: 2023-09-27 18:24:53
大家好,我有一个要求,我必须分配多个键,对于多个键,我必须分配多个值
我的要求如下。我为每个员工提供EmpID
、PayYr
和PayID
。
假设我按如下方式获取数据:
EmpID 1000 1000 1000 1000
PayYr 2011 2011 2011 2012
PayID 1 2 3 1
我想拥有我的字典,以便具有键值结果的字典如下所示:
1000 - 2011 - 1,2,3
1000 - 2012 - 1
我尝试了以下一些事情
public struct Tuple<T1, T2>
{
public readonly T1 Item1;
public readonly T2 Item2;
public Tuple(T1 item1, T2 item2)
{
Item1 = item1;
Item2 = item2;
}
}
示例代码
for (int empcnt = 0; empcnt < iEmpID.Length; empcnt++)
{
for (int yrcnt = 0; yrcnt < ipayYear.Length; yrcnt++)
{
List<int> lst1 = new List<int>();
var key1 = new Tuple<int, int>(iEmpID[empcnt], ipayYear[yrcnt]);
if (!dictAddValues.ContainsKey(key1))
{
dictAddValues.Add(key1, lst1);
lst1.Add(lst[yrcnt]);
}
}
}
但是我没有按照我的要求得到我的结果,所以任何人都可以帮助我。
就个人而言,我可能会使用字典词典,例如 IDictionary<int, IDictionary<int, IList<int>>>
.不,我不完全确定您打算如何访问或促进这些数据;这将对我的建议的效率产生很大的影响。从好的方面来说,它将允许您 - 相对容易 - 访问数据,当且仅当您按照设置字典的顺序访问数据时.
(再三考虑,类型声明本身是如此丑陋和毫无意义,你可能想跳过我上面所说的。
如果您相当随机地访问字段,也许一个简单的非规范化ICollection<Tuple<int, int, int>>
(或等效项(将不得不完成这个技巧,并根据需要在应用程序的其他部分中进行聚合。LINQ 在这方面可以提供很多帮助,尤其是它的聚合、分组和查找功能。
更新:希望这能澄清它:
var outerDictionary = new Dictionary<int, Dictionary<int, List<int>>>();
/* fill initial values
* assuming that you get your data row by row from an ADO.NET data source, EF, or something similar. */
foreach (var row in rows) {
var employeeId = (int) row["EmpID"];
var payYear = (int) row["PayYr"];
var payId = (int) row["PayID"];
Dictionary<int, int> innerDictionary;
if (!outerDictionary.TryGet(employeeId, out innerDictionary)) {
innerDictionary = new Dictionary<int, int>();
outerDictionary.Add(employeeId, innerDictionary);
}
List<int> list;
if (!innerDictionary.TryGet(payYear)) {
list = new List<int>();
innerDictionary.Add(payYear, list);
}
list.Add(payId);
}
/* now use it, e.g.: */
var data = outerDictionary[1000][2011]; // returns a list with { 1, 2, 3 }
不过,请持保留态度;请参阅评论。
我认为你错过了比较器部分。 看看下面的文章是否有帮助。
具有自定义键的字典
http://www.codeproject.com/Articles/23610/Dictionary-with-a-Custom-Key
如果键是类的一部分,则使用 KeyedCollection。
它是一个字典,其中键派生自对象。
在封面下是字典。D 不必重复键和值中的键。
为什么要冒险,键中的键与值不同。不必在内存中复制相同的信息。
键控集合类
用于公开组合键的索引器
using System.Collections.ObjectModel;
namespace IntIntKeyedCollection
{
class Program
{
static void Main(string[] args)
{
UInt16UInt16O Emp1 = new UInt16UInt16O(34, 1990);
Emp1.PayIDs.Add(1);
Emp1.PayIDs.Add(2);
UInt16UInt16O Emp2 = new UInt16UInt16O(34, 1990, new List<byte>{3,4});
if (Emp1 == Emp2) Console.WriteLine("same");
if (Emp1.Equals(Emp2)) Console.WriteLine("Equals");
Console.WriteLine("Emp1.GetHashCode " + Emp1.GetHashCode().ToString());
UInt16UInt16OCollection Employees = new UInt16UInt16OCollection();
Employees.Add(Emp1);
//this would fail
//Employees.Add(Emp2);
Employees.Add(new UInt16UInt16O(35, 1991, new List<byte> { 1 } ));
Employees.Add(new UInt16UInt16O(35, 1992, new List<byte> { 1, 2 } ));
Employees.Add(new UInt16UInt16O(36, 1992));
Console.WriteLine(Employees.Count.ToString());
// reference by ordinal postion (note the is not the long key)
Console.WriteLine(Employees[0].GetHashCode().ToString());
// reference by Int32 Int32
Console.WriteLine(Employees[35, 1991].GetHashCode().ToString());
Console.WriteLine("foreach");
foreach (UInt16UInt16O emp in Employees)
{
Console.WriteLine(string.Format("HashCode {0} EmpID {1} Year {2} NumCodes {3}", emp.GetHashCode(), emp.EmpID, emp.Year, emp.PayIDs.Count.ToString()));
}
Console.WriteLine("sorted");
foreach (UInt16UInt16O emp in Employees.OrderBy(e => e.EmpID).ThenBy(e => e.Year))
{
Console.WriteLine(string.Format("HashCode {0} EmpID {1} Year {2} NumCodes {3}", emp.GetHashCode(), emp.EmpID, emp.Year, emp.PayIDs.Count.ToString()));
}
}
public class UInt16UInt16OCollection : KeyedCollection<UInt16UInt16S, UInt16UInt16O>
{
// This parameterless constructor calls the base class constructor
// that specifies a dictionary threshold of 0, so that the internal
// dictionary is created as soon as an item is added to the
// collection.
//
public UInt16UInt16OCollection() : base(null, 0) { }
// This is the only method that absolutely must be overridden,
// because without it the KeyedCollection cannot extract the
// keys from the items.
//
protected override UInt16UInt16S GetKeyForItem(UInt16UInt16O item)
{
// In this example, the key is the part number.
return item.UInt16UInt16S;
}
// indexer
public UInt16UInt16O this[UInt16 EmpID, UInt16 Year]
{
get { return this[new UInt16UInt16S(EmpID, Year)]; }
}
}
public struct UInt16UInt16S
{ // required as KeyCollection Key must be a single item
// but you don't reaaly need to interact with Int32Int32s
public readonly UInt16 EmpID, Year;
public UInt16UInt16S(UInt16 empID, UInt16 year) { this.EmpID = empID; this.Year = year; }
}
public class UInt16UInt16O : Object
{
// implement you properties
public UInt16UInt16S UInt16UInt16S { get; private set; }
public UInt16 EmpID { get { return UInt16UInt16S.EmpID; } }
public UInt16 Year { get { return UInt16UInt16S.Year; } }
public List<byte> PayIDs { get; set; }
public override bool Equals(Object obj)
{
//Check for null and compare run-time types.
if (obj == null || !(obj is UInt16UInt16O)) return false;
UInt16UInt16O item = (UInt16UInt16O)obj;
return (this.EmpID == item.EmpID && this.Year == item.Year);
}
public override int GetHashCode() { return ((UInt32)EmpID << 16 | Year).GetHashCode() ; }
public UInt16UInt16O(UInt16 EmpID, UInt16 Year)
{
UInt16UInt16S uInt16UInt16S = new UInt16UInt16S(EmpID, Year);
this.UInt16UInt16S = uInt16UInt16S;
PayIDs = new List<byte>();
}
public UInt16UInt16O(UInt16 EmpID, UInt16 Year, List<byte> PayIDs)
{
UInt16UInt16S uInt16UInt16S = new UInt16UInt16S(EmpID, Year);
this.UInt16UInt16S = uInt16UInt16S;
this.PayIDs = PayIDs;
}
}
}
}
在Tuple
结构中实现 Equals 和 GetHashCode:
public override bool Equals(object obj)
{
if (!(obj is Tuple<T1, T2>))
return false;
var t = (Tuple<T1, T2>)obj
return (this.Item1 == t.Item1 && this.Item2 == t.Item2);
}
public override int GetHashCode()
{
return (Item1 ^ Item2 );
}
我不能 100% 确定您要用作键的确切数据。 我想2? 2 整数值? 这就是我将在下面假设的,但是如果您想要三个或类型不同,请相应地进行调整。 我建议如下(步骤 1 是必需的,步骤 2 是可选的,但我会这样做(
步骤 1 创建自己的键结构,用作标准字典中的键。 为您的值提供 2 个属性(或三个,无论如何(,这些属性将充当键,和/或获取/设置这些值的构造函数。
指定 GetHashCode 方法。 像这样:
public override int GetHashCode()
{
unchecked
{
return (_empId * 397) ^ _payYr;
}
}
注意:是的,您可以使用元组。 元组 . . .并不像第一个看起来那么酷。 您的属性名称将为 Item1,依此类推。 不是很清楚。 而且您通常最终想要覆盖并尽快添加内容。 从头开始。
喜欢这个: 公共结构 支付密钥 {
private int _empId
private int _payYr;
public PayKey (int empId, int payYr) {
_empId = empId;
_payYr = payYr;
}
public override int GetHashCode()
{
{
return (_empId * 83) ^ _payYr;
}
}
}
注意:如果要在组合键中使用的多个值中的任何一个是引用类型,则可能应该创建一个类而不是结构。 如果是这样,您还需要覆盖Equals
才能使其作为字典键正常工作。
public override bool Equals( object pkMaybe ){
if( pkMaybe is PayKey ) {
PayKey pk = (PayKey) pkMaybe ;
return _empId = pk.EmpId && _payYr = pk.PayYr;
}
else {
return false;
}
}
(如果尚未添加公共属性,请为键值添加公共属性。
或者,如果您创建我在下面提到的自定义字典,则使用IEqualityComparer会很方便。 (基本上,如果您使用类作为键,则必须确保字典将两个相同的 PayKey 对象视为"相等"。 默认情况下,即使值相等,它们也是对不同对象的引用,因此框架会认为它们不相等(
步骤 2 创建一个继承自字典的类。 给它两个额外的方法:
- 一种 add 方法,该方法采用两个关键参数以及要添加的值。 在里面,您将构造一个键结构并调用其基 add 方法,键对象作为键,当然你的值作为值。
- 项目的重载或根据需要命名。 此方法将密钥的 2 个整数作为参数,并返回项目。 在此方法中,你将构造一个键结构,并使用键结构调用基项方法来检索对象。
- 此外,为了您的最终方便,您可能希望将其他重载添加到字典中,您可以在其中指定键值,而不必每次都构造自己的键结构。 例如,我可能要做的第一件事是添加一个 KeyExists 属性,该属性采用我的两个键值。
尝试查看 Microsoft 包含 MultiValueDictionary 类型的 https://www.nuget.org/packages/Microsoft.Experimental.Collections。
多值字典是关联单个的通用字典 具有一个或多个值的键。可以添加和删除值 独立地。