3

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

我的要求如下。我有EmpIDPayYr并且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]);
            }
        }

    }

但我没有得到我需要的结果,所以任何人都可以帮助我。

4

6 回答 6

3

就个人而言,我可能会使用字典词典,例如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 }

不过,请带上一粒盐;见评论。

于 2012-02-17T13:49:44.373 回答
1

我认为您缺少比较器部分。看看下面的文章是否有帮助。

带有自定义键的字典

http://www.codeproject.com/Articles/23610/Dictionary-with-a-Custom-Key

于 2012-02-17T13:15:58.980 回答
1

如果密钥是类的一部分,则使用 KeyedCollection。
它是一个字典,其中键是从对象派生的。
在封面下是字典。D 不必在Key 和Value 中重复key。
为什么要冒险在 Key 中的 key 与 Value 不同。不必在内存中复制相同的信息。

KeyedCollection 类

索引器以公开复合键

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;
            }
        }
    }
}
于 2012-10-02T00:10:31.787 回答
0

我不能 100% 确定您想用作密钥的确切数据。我想2?2 整数值?这就是我在下面假设的,但如果你想要三个或类型不同,只需相应地调整。我建议以下(第 1 步是必要的,第 2 步是可选的,但我会这样做)

步骤 1创建自己的键结构,用作标准字典中的键。为您的值提供 2 个属性(或三个,等等)作为键,和/或构造函数获取/设置这些值。

指定 GetHashCode 方法。就像是:

public override int GetHashCode()
{
  unchecked
  {
    return (_empId * 397) ^ _payYr;
  }
}

注意: 是的,您可以使用元组。元组。. . 不像第一个看起来那么酷。您的属性名称将是 Item1 等。不是很清楚。而且您通常最终想要覆盖并尽快添加内容。只是从头开始。

像这样: public struct PayKey {

  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创建一个继承自 Dictionary 的类。给它两个额外的方法:

  • 一个 add 方法,它接受您的两个关键参数以及您要添加的值。在内部,您将构造一个键结构并调用它的基本 add 方法,键对象作为键,您的值当然作为值。
  • item 的重载或根据需要命名。此方法将把您的密钥的 2 个整数作为参数,并返回该项目。在此方法中,您将构造一个键结构,并使用键结构调用基本项目方法来检索对象。
  • 此外,为了您最终的方便,您可能希望将其他重载添加到您的字典中,您可以在其中指定您的键值,而不必每次都构建您自己的键结构。例如,我可能要做的第一件事是添加一个 KeyExists 属性,该属性采用我的两个键值。
于 2012-02-17T13:30:48.010 回答
0

您需要在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 );
    }
于 2012-02-17T13:31:44.970 回答
0

尝试查看来自 Microsoft 的https://www.nuget.org/packages/Microsoft.Experimental.Collections,其中包含 MultiValueDictionary 类型。

MultiValueDictionary 是一个通用字典,它将单个键与一个或多个值相关联。可以独立添加和删除值。

于 2014-11-12T16:43:33.943 回答