1

Continuing from the Q&As dealing with looping through an object's properties (Using LINQ to loop through inner class properties in outer class collection), is it possible to populate a dictionary:

Dictionary<ComplexKey,IEnumerable<double>> answer;

For example,

  • answer[1,1,1,"MeasurementA"] = {2.0, 2.1}
  • answer[1,1,1,"MeasurementB"] = {3.0, 3.1}
  • answer[1,1,1,"MeasurementC"] = {4.0, 4.1}
  • answer[1,1,2,"MeasurementA"] = {5.0, 5.1}

Given the structure:

class MainClass {
  List<Class1> list
}

class Class1 {
  // a number of fields including...
  int PropertyA { get; set; }
  int PropertyB { get; set; }
  Dictionary<int, Class2> dict { get; set; }
}

class Class2 {
  // a number of fields all of type double...
  double MeasurementA { get; set; }
  double MeasurementB { get; set; }
  double MeasurementC { get; set; }
}

struct ComplexKey {
  public int class1PropA;
  public int class1PropB;
  public int class1DictKey;  
  public string class2PropName;
}

Given data:

MainClass mainClass = new MainClass();
mainClass.list = new List<Class1>() {
  new Class1() {
    PropertyA = 1,
    PropertyB = 1, 
    dict = new Dictionary<int,Class2>() {
      { 1, new Class2() { MeasurementA = 2.0, MeasurementB = 3.0, MeasurementC = 4.0 }},
      { 2, new Class2() { MeasurementA = 5.0, MeasurementB = 6.0, MeasurementC = 7.0 }}
    }
  },
  new Class1() { 
    PropertyA = 1,
    PropertyB = 1, 
    dict = new Dictionary<int,Class2>() {
      { 1, new Class2() { MeasurementA = 2.1, MeasurementB = 3.1, MeasurementC = 4.1 }},
      { 2, new Class2() { MeasurementA = 5.1, MeasurementB = 6.1, MeasurementC = 7.1 }}
    }
  }
};

Noting in this example, Class1.PropertyA and Class1.PropertyB are consistently set to "1" for brevity. (This is not the case in the real world data set).

I believe populating the Dictionary "answer" would require, grouping the mainclass.list by PropertyA, PropertyB, and dict.Key before accessing the properties and values within dict.Values (i.e. instances of Class2).

I thought LINQ was the answer but have been stuck for many weeks. Any direction would be appreciated.

Thanks & regards Shannon

shansen
  • 265
  • 4
  • 14
  • 1
    Please show what have you tried so far so that some one can help you. – vikas Oct 06 '14 at 05:42
  • In the related query @KonradKokosa provided the partial answer: [link] (http://stackoverflow.com/a/21109689/3176705). To include the outer iteration variable reference I am trying comprehension syntax: – shansen Oct 06 '14 at 06:38
  • var flds = from p in typeof(Class2).GetProperties(BindingFlags.Public | BindingFlags.Instance) from c1 in mainClass.list from d in c1.dict select new { class1PropA = c1.PropertyA, class1PropB = c1.PropertyB, class1DictKey = d.Key, class2PropName = p.Name, val = p.GetValue(d, null) }; – shansen Oct 06 '14 at 06:47
  • Apologies. I am having trouble posting the code as CODE. – shansen Oct 06 '14 at 06:50
  • What's is an output? I mean the collection of doubles (as value in dictionary). I completetly can't get it. –  Oct 06 '14 at 07:01
  • @pwas You are correct, the output is a dictionary with a collection of doubles as the value. The real-life example will create an instance of another Class which will calculate a distribution from the collection of doubles. – shansen Oct 06 '14 at 07:07

1 Answers1

0

Based on the question you linked, it seems you're looking for something like this:

var answer = 
 typeof(Class2).GetProperties(BindingFlags.Public | BindingFlags.Instance)
               .SelectMany(pi => mainClass.list.SelectMany(c1 => 
                    c1.dict.Select(i => new {
                                                c1.PropertyA, 
                                                c1.PropertyB, 
                                                i.Key,
                                                i.Value}))
                            .Select(p => new {
                                                Key = new ComplexKey {
                                                    class1PropA = p.PropertyA,
                                                    class1PropB = p.PropertyB,
                                                    class1DictKey = p.Key,
                                                    class2PropName = pi.Name},
                                                Value = (double)pi.GetValue(p.Value)}))
                .GroupBy(grp => grp.Key)
                .ToDictionary(grp => grp.Key, grp => grp.Select(x => x.Value));

answer is now

enter image description here

Note: If you use a custom type (like ComplexKey) as key in a Dictionary, you should implement a proper GetHashCode and Equals method, since these methods are used to distinguish the keys inside the Dictionary.

sloth
  • 99,095
  • 21
  • 171
  • 219
  • This is brilliant! The picture looks like it is a screenshot from LinqPad. Based on the LinqPad "Custom methods and types" sample, I can see the classes & struct can be added outside the Main() block. The mainclass instanciated inside the Main() block along with your LINQ query. However, on the line `Value = (double)pi.GetValue(p.Value)}))` LinqPad returns the error `No overload for method 'GetValue' takes '1' arguments`. Any ideas where I've gone wrong? – shansen Oct 06 '14 at 09:47
  • Changing the line `Value = (double)pi.GetValue(p.Value)` to `Value = (double)pi.GetValue(p.Value,null)`, results "Query successful". However, no results are displayed using Rich Text or Data Grids formats. Any ideas? – shansen Oct 06 '14 at 09:55
  • Found it! `answer.Dump();` – shansen Oct 06 '14 at 10:01