Question

Salut à tous je vais avoir une exigence où je dois assigner des touches multiples et que plusieurs clés que je dois affecter plusieurs valeurs

Mon exigence est la suivante. J'ai EmpID, PayYr et PayID pour chaque employé.

On suppose que je reçois mes données comme suit:

EmpID  1000    1000  1000   1000
PayYr  2011    2011  2011   2012
PayID    1      2     3      1

Je voudrais avoir mon dictionnaire pour que le dictionnaire avec un résultat de valeur clé est la suivante:

1000 - 2011 - 1,2,3
1000 - 2012 - 1

J'ai essayé quelque chose comme suit

public struct Tuple<T1, T2>
{
    public readonly T1 Item1;
    public readonly T2 Item2;

    public Tuple(T1 item1, T2 item2)
    {
        Item1 = item1;
        Item2 = item2;
    }
}

Exemple de code

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

    }

Mais je ne reçois pas mon résultat que je nécessaire afin que peut aider moi.

Était-ce utile?

La solution

Personnellement, je serais probablement utiliser un dictionnaire de dictionnaires, par exemple IDictionary<int, IDictionary<int, IList<int>>>. Non je ne suis pas tout à fait sûr de savoir comment vous avez l'intention d'accéder ou de faciliter ces données; que ont un impact important sur la façon dont ma suggestion est efficace. A la hausse, il vous permettra de - relativement facilement - données d'accès, si et seulement si vous pouvez y accéder dans l'ordre que vous configurez vos dictionnaires
. (Sur la deuxième pensée, simplement la déclaration de type lui-même est si laid et vide de sens, vous pouvez sauter ce que je disais ci-dessus.)

Si vous accédez à des champs plutôt au hasard, peut-être d'un simple dénormaliser ICollection<Tuple<int, int, int>> (ou équivalent) devra faire l'affaire, avec l'agrégation dans d'autres parties de votre application au besoin. LINQ peut aider ici beaucoup, en particulier son agrégation, le regroupement et rechercher des caractéristiques.

Mise à jour: Espérons que cette clarifie:

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 }

Prenez-le avec un grain de sel bien; voir le commentaire.

Autres conseils

Je pense que vous êtes absent la pièce Comparer. Voir si l'article aide ci-dessous.

Dictionnaire avec une touche personnalisée

http://www.codeproject.com/Articles/ 23610 / Dictionnaire-avec-un-Custom-Key

Si la clé fait partie de la classe puis utilisez KeyedCollection.
Il est un dictionnaire où la clé est dérivée de l'objet.
Sous les couvertures, il est le dictionnaire. D Ne pas répéter la clé dans la clé et la valeur.
Pourquoi prendre une chance la clé n'est pas la même chose dans la clé de la valeur. Ne pas répéter les mêmes informations en mémoire.

KeyedCollection classe

indexeur pour exposer la clé composite

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

Vous devez equals et GetHashCode dans votre struct Tuple:

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

Je ne suis pas sûr à 100% de la exacte des données que vous souhaitez utiliser comme clé. Je pense que 2? 2 valeurs entières? Voilà ce que je vais supposer ci-dessous, mais si vous voulez trois ou le type est différent, juste ajuster en conséquence. Je suggère ce qui suit (étape 1 est nécessaire, l'étape 2 est facultative, mais je le ferais)

Étape 1 Créer votre propre struct clé, à utiliser comme clé dans un dictionnaire standard. Donnez-lui 2 propriétés (ou trois, peu importe) pour vos valeurs qui agissent comme la clé, et / ou une prise / réglage de ces valeurs Constructor.

Spécifie une méthode GetHashCode. Quelque chose comme:

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

Remarque: Oui, peut utiliser un tuple. Tuples. . . ne sont pas aussi cool que la première semble. Vos noms de propriété seront Item1, etc. pas très claire. Et vous finissez souvent voulu ajouter des choses majeur et assez tôt. Il suffit de commencer à partir de zéro.

Comme ceci: PayKey public struct {

  private int _empId
  private int _payYr;

  public PayKey (int empId, int payYr) {
    _empId = empId;
    _payYr = payYr;
}

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

}

Remarque: Si l'un de vos multiples valeurs que vous souhaitez utiliser dans votre clé combinée sont les types de référence, vous devriez probablement créer une classe au lieu d'une struct. Si oui, vous aurez également besoin de dérogation Equals pour que cela fonctionne correctement comme clé de dictionnaire.

public override bool Equals( object pkMaybe ){
    if( pkMaybe is PayKey ) {
        PayKey pk = (PayKey) pkMaybe ;
        return _empId = pk.EmpId && _payYr = pk.PayYr;
    }
    else {
        return false;
    }
}

(Et ajouter des propriétés publiques pour vos valeurs clés si vous avez pas déjà).

Ou, si vous créez le dictionnaire personnalisé que je mentionne ci-dessous, il serait l'utilisation pratique d'un IEqualityComparer . (En fait, si vous utilisez une classe comme la clé, vous devez vous assurer que le dictionnaire verra deux objets de PayKey identique à « égal ». Par défaut, même avec des valeurs égales, ils sont des références à des objets différents, de sorte que le cadre examinera les pas égaux)

Étape 2 Créer une classe héritant de dictionnaire. Donnez-lui deux méthodes supplémentaires:

  • Une méthode add qui prend vos deux paramètres clés, ainsi que la valeur que vous voulez ajouter. A l'intérieur, vous construisez un de vos struct clés et appeler sa méthode add de base, avec l'objet clé comme la clé et votre valeur bien sûr que la valeur.
  • une surcharge pour l'élément ou sa désignation que vous le souhaitez. Cette méthode prend comme paramètres les 2 entiers de votre clé, et renvoyer l'article. A l'intérieur de cette méthode vous construire un de vos struct clés, et appelez la méthode de l'élément de base avec la struct clé pour récupérer l'objet.
  • En outre, pour votre commodité éventuelle, vous aurez probablement envie d'ajouter d'autres à votre dictionnaire surcharges, où vous pouvez spécifier votre valeurs clés, plutôt que d'avoir à construire votre propre clé struct chaque fois. Par exemple, la première chose que je serais probablement faire est d'ajouter une propriété KeyExists qui a pris mes deux valeurs clés.

Essayez de jeter un oeil à https://www.nuget.org/packages/Microsoft .Experimental.Collections de Microsoft qui contient le type MultiValueDictionary.

MultiValueDictionary est un dictionnaire générique qui associe un seul clé avec une ou plusieurs valeurs. Les valeurs peuvent être ajoutés et supprimés indépendamment.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top