具有多个键和每个键的多个值的字典

本文关键字:字典 | 更新日期: 2023-09-27 18:24:53

大家好,我有一个要求,我必须分配多个键,对于多个键,我必须分配多个值

我的要求如下。我为每个员工提供EmpIDPayYrPayID

假设我按如下方式获取数据:

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。

多值字典是关联单个的通用字典 具有一个或多个值的键。可以添加和删除值 独立地。