3

こんにちは、複数のキーを割り当てる必要があり、その複数のキーには複数の値を割り当てる必要があります

私の要件は次のとおりです。私は各従業員のため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]);
            }
        }

    }

しかし、必要な結果が得られないので、誰か助けてください。

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

キーがクラスの一部である場合は、KeyedCollection を使用します。
キーがオブジェクトから派生する辞書です。
カバーの下には辞書があります。D キーと値でキーを繰り返す必要がない。
キーが値とキーで同じではない可能性があります。メモリ内で同じ情報を複製する必要はありません。

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 に答える
1

Comparer の部分が欠けていると思います。以下の記事が役立つかどうかを確認してください。

カスタム キーを使用したディクショナリ

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

于 2012-02-17T13:15:58.980 に答える
0

キーとして使用したい正確なデータについて 100% 確信があるわけではありません。私は2だと思いますか?2 整数値? 以下はそのように仮定しますが、3つが必要な場合やタイプが異なる場合は、それに応じて調整してください. 次のことをお勧めします (ステップ 1 は必須です。ステップ 2 はオプションですが、私ならそうします)。

ステップ 1標準ディクショナリでキーとして使用される独自のキー構造体を作成します。キーとして機能する値の 2 つのプロパティ (または 3 つなど)、および/またはそれらの値を取得/設定するコンストラクターを指定します。

GetHashCode メソッドを指定します。何かのようなもの:

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

注: はい、 Tuple を使用できます。タプル。. . 最初のようにクールではありません。プロパティ名は 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を使用すると便利です。(基本的に、クラスをキーとして使用する場合、ディクショナリが 2 つの同一の PayKey オブジェクトを「等しい」と見なすようにする必要があります。デフォルトでは、値が等しい場合でも、それらは異なるオブジェクトへの参照であるため、フレームワークはそれらは等しくありません)

ステップ 2 Dictionary から継承するクラスを作成します。2 つの追加メソッドを指定します。

  • 2 つの主要なパラメーターと追加する値を受け取る add メソッド。内部では、キー構造体の 1 つを構築し、その基本 add メソッドを呼び出します。キー オブジェクトをキーとして、もちろん値を値として使用します。
  • item のオーバーロードまたは必要に応じて名前を付けます。このメソッドは、キーの 2 つの整数をパラメーターとして取り、アイテムを返します。このメソッド内で、キー構造体の 1 つを作成し、キー構造体を使用して基本項目メソッドを呼び出してオブジェクトを取得します。
  • さらに、最終的な利便性のために、毎回独自のキー構造体を作成するのではなく、キー値を指定できる他のオーバーロードをディクショナリに追加することをお勧めします。たとえば、おそらく最初に行うことは、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 に答える