0

I have this kind of data type:

Dictionary<string, List<Dictionary<string, List<Dictionary<string, List<Dictionary<string, string>>>>>>>

and whenever I'm trying to reach the very bottom parts of it to count some stuff there I'm having an error that I can't use foreach on KeyValuePairs:

foreach statement cannot operate on variables of type 'KeyValuePair<string, List<Dictionary<string, List<Dictionary<string, string>>>>>' because 'KeyValuePair<string, List<Dictionary<string, List<Dictionary<string, string>>>>>' does not contain a public definition for 'GetEnumerator' [RestService]

Here's that part of loops:

void GetMetricsCount(List<Dictionary<string, List<Dictionary<string, List<Dictionary<string, string>>>>>> device, Dictionary<string, string> results_holder)
        {
            // count all metrics
            foreach (var type_element in device)
            {   
                foreach (var group_element in type_element)
                {   
                    foreach (var wtf in group_element)

Now it would be actually really good if there is any better way to deal with that kind of complicated data structures in C# and I would really appreciate if anyone tells me how because this is yuck!

UPDATE: I'm sorry for it but I've cut some code above while copying it here. Here's the answer to frustrations with non-matching types:

        // Get overview of metrics for last 24h
    public Dictionary<string, Dictionary<string, string>> GetMetricsOverview()
    {
        var overview_results = new Dictionary<string, Dictionary<string, string>>();

        // get total item count for each metric group and add to holder
        void GetMetricsCount(List<Dictionary<string, List<Dictionary<string, List<Dictionary<string, string>>>>>> device, Dictionary<string, string> results_holder)
        {
            // count all metrics
            foreach (var type_element in device)
            {   
                foreach (var group_element in type_element.Values)
                {   
                    foreach (var wtf in group_element)
                    results_holder.Add(group_element.Key + "_count", group_element.Value.Count.ToString());
                }
            }
            //
        }
        //
Roman
  • 733
  • 1
  • 5
  • 14
  • Possible duplicate of [What is the best way to iterate over a Dictionary in C#?](https://stackoverflow.com/questions/141088/what-is-the-best-way-to-iterate-over-a-dictionary-in-c) – omajid Feb 14 '18 at 16:35
  • The type in the first snippet does not match the type in the method definition. – Joel Coehoorn Feb 14 '18 at 17:25
  • Ah, recursion. That would do it :) – Joel Coehoorn Feb 14 '18 at 17:55
  • This thing is now really hard to maintain. I've come from Python world and in there you're all set with dot notation because everything is an object. What would be your tips to make it in better way @JoelCoehoorn ? – Roman Feb 14 '18 at 17:59
  • 1
    The correct thing to do would be to define actual classes, rather than using a dictionary to represent all your data. I think the actual problem here is one of how to represent the data, not of how to use the dictionaries you've created. – dodexahedron Feb 14 '18 at 18:28

1 Answers1

7

The outer loop looks like this:

foreach (var type_element in device)

With this loop, the type_element variable is a Dictionary<string, List<Dictionary<...>>> reference. It sounds like you're expecting to skip a level and go straight to the next List<Dictionary<...>>.

Maybe what you want is this:

foreach (var type_element in device)
{   
    foreach (var group_element in type_element.Values)

Here, type_element.Values is still a collection of Lists, so the group_element variable is still a List<Dictionary<string, List<Dictionary<string, string>>>>.

Just for completeness, that means the wtf variable at your next level is still a Dictionary<string, List<Dictionary<string, string>>> ... which is kind of crazy, if you think about it; the variable definitely lives up to its name. Are you sure you wouldn't rather define a class or two to get some compile-time type-safety around this data?

To get all the way to the inner-most Dictionary, you need this:

foreach (var type_element in device)
{ 
    foreach (var type_lists in type_element.Values)
    {   
        foreach(var group_element in type_lists)
        {
            foreach(var group_lists in group_element.Values)
            {
                 foreach (var wtf in group_lists.Values)
                 {
                      //...

You end up with a Dictionary<string, string> for the wtf variable. But again... this whole thing is kind of ridiculous. It looks a bit like you're trying to re-create a perfectly good type system using Dictionaries.

You may also want to look at the SelectMany() method, which can help reduce and simplify this code.

Perhaps you are trying to shoehorn Python idioms, especially associative object syntax (item["property"]), into .Net. This is a mistake. .Net doesn't really do associative objects. You want to actually define a class for this.

Joel Coehoorn
  • 399,467
  • 113
  • 570
  • 794
  • Off by one level. The top is also a `List`. I'd say it looks like he's trying to iterate over the lists but forgetting about the dictionaries. – Vilx- Feb 14 '18 at 17:00
  • @Vilx- it's confusing because of type in the first snippet is not the same as the type in the function definition. That has led to some mistakes in this answer, which I _will_ (eventually -- this is confusing) sort out now the I've noticed the discrepancy in the question. – Joel Coehoorn Feb 14 '18 at 17:04
  • 2
    Should be accurate now. – Joel Coehoorn Feb 14 '18 at 17:20