Home > Enterprise >  How do I properly loop through two nested json structures in golang
How do I properly loop through two nested json structures in golang

Time:01-13

I am new to Go. I have 2 identical json nested structures that are each populated with the output of 2 api calls. One call fetches cpu and the other memory metrics. I can unmarshal each of them individually and print out the project name and values of cpu and memory, albeit in 2 separate code blocks. The problem I am having is that I would like to print out both cpu and memory metrics on the same line, next to their project name.

Here is the code I am using to print out the CPU metrics by Project. It creates a nice CSV formatted output:

// CPU Metrics
// Loop through the data for the Month, Project, CPU requests, and CPU Usage
   fmt.Println("Month, Project, CPU Request(Core hours), CPU Usage(Core hours)\n")
   for _, value_cpu := range rh_values_cpu.Data {
       for _, val_cpu := range value_cpu.Projects {
        str := val_cpu.Project
        s := strings.Contains(str, "openshift")
         if s == true  {
            continue
        }
           fmt.Printf("%s, %s, ", value_cpu.Date, val_cpu.Project)
           for _, v_cpu := range val_cpu.Values {
           fmt.Printf("%.1f, %.1f\n", v_cpu.Request.Value, v_cpu.Usage.Value)
         }
       }
    }

I have similar code for the memory metrics which also works fine.

Here is the code I am using to loop through the two json structures. I suspect that I'm not using the nested loops properly or need to solve the problem differently.

// CPU & Memory Metrics
// Loop through the data for the Month, Project, CPU requests, CPU Usage, Memory requests, and Memory Usage
   fmt.Println("Month, Project, CPU Request(Core hours), CPU Usage(Core hours) Memory Request(mBytes), Memory Usage(mBytes)\n")
   for _, value_cpu := range rh_values_cpu.Data {
    for _, value_mem := range rh_values_MEM.Data {
      for _, val_cpu := range value_cpu.Projects {
        for _, val_mem := range value_mem.Projects {
         str := val_cpu.Project
         s := strings.Contains(str, "openshift")
          if s == true  {
            continue
        }
           fmt.Printf("%s, %s, ", value_cpu.Date, val_cpu.Project)
           for _, v_cpu := range val_cpu.Values {
              fmt.Printf("%.1f, %.1f ", v_cpu.Request.Value, v_cpu.Usage.Value)
                for _,v_mem := range val_mem.Values {
                  fmt.Printf("%.1f, %.1f\n", v_mem.Request.Value, v_mem.Usage.Value)
       }
    }
  }
}
}
}

And here is of one the json structures:

type RH_Output_MEM struct {
    Meta struct {
        Count    int    `json:"count"`
        Others   int    `json:"others"`
        Currency string `json:"currency"`
        Filter   struct {
            Resolution     string `json:"resolution"`
            TimeScopeValue string `json:"time_scope_value"`
            TimeScopeUnits string `json:"time_scope_units"`
            Limit          int    `json:"limit"`
            Offset         int    `json:"offset"`
        } `json:"filter"`
        GroupBy struct {
            Project []string `json:"project"`
        } `json:"group_by"`
        OrderBy struct {
        } `json:"order_by"`
        Exclude struct {
        } `json:"exclude"`
        Total struct {
            Usage struct {
                Value float64 `json:"value"`
                Units string  `json:"units"`
            } `json:"usage"`
            Request struct {
                Value float64 `json:"value"`
                Units string  `json:"units"`
            } `json:"request"`
            Limit struct {
                Value float64 `json:"value"`
                Units string  `json:"units"`
            } `json:"limit"`
            Capacity struct {
                Value float64 `json:"value"`
                Units string  `json:"units"`
            } `json:"capacity"`
            Infrastructure struct {
                Raw struct {
                    Value float64 `json:"value"`
                    Units string  `json:"units"`
                } `json:"raw"`
                Markup struct {
                    Value float64 `json:"value"`
                    Units string  `json:"units"`
                } `json:"markup"`
                Usage struct {
                    Value float64 `json:"value"`
                    Units string  `json:"units"`
                } `json:"usage"`
                Distributed struct {
                    Value float64 `json:"value"`
                    Units string  `json:"units"`
                } `json:"distributed"`
                Total struct {
                    Value float64 `json:"value"`
                    Units string  `json:"units"`
                } `json:"total"`
            } `json:"infrastructure"`
            Supplementary struct {
                Raw struct {
                    Value float64 `json:"value"`
                    Units string  `json:"units"`
                } `json:"raw"`
                Markup struct {
                    Value float64 `json:"value"`
                    Units string  `json:"units"`
                } `json:"markup"`
                Usage struct {
                    Value float64 `json:"value"`
                    Units string  `json:"units"`
                } `json:"usage"`
                Distributed struct {
                    Value float64 `json:"value"`
                    Units string  `json:"units"`
                } `json:"distributed"`
                Total struct {
                    Value float64 `json:"value"`
                    Units string  `json:"units"`
                } `json:"total"`
            } `json:"supplementary"`
            Cost struct {
                Raw struct {
                    Value float64 `json:"value"`
                    Units string  `json:"units"`
                } `json:"raw"`
                Markup struct {
                    Value float64 `json:"value"`
                    Units string  `json:"units"`
                } `json:"markup"`
                Usage struct {
                    Value float64 `json:"value"`
                    Units string  `json:"units"`
                } `json:"usage"`
                Distributed struct {
                    Value float64 `json:"value"`
                    Units string  `json:"units"`
                } `json:"distributed"`
                Total struct {
                    Value float64 `json:"value"`
                    Units string  `json:"units"`
                } `json:"total"`
            } `json:"cost"`
        } `json:"total"`
    } `json:"meta"`
    Links struct {
        First    string      `json:"first"`
        Next     string      `json:"next"`
        Previous interface{} `json:"previous"`
        Last     string      `json:"last"`
    } `json:"links"`
    Data []struct {
        Date     string `json:"date"`
        Projects []struct {
            Project string `json:"project"`
            Values  []struct {
                Date    string `json:"date"`
                Project string `json:"project"`
                Usage   struct {
                    Value float64 `json:"value"`
                    Units string  `json:"units"`
                } `json:"usage"`
                Request struct {
                    Value float64 `json:"value"`
                    Units string  `json:"units"`
                } `json:"request"`
                Limit struct {
                    Value float64 `json:"value"`
                    Units string  `json:"units"`
                } `json:"limit"`
                Capacity struct {
                    Value float64 `json:"value"`
                    Units string  `json:"units"`
                } `json:"capacity"`
                Classification string   `json:"classification"`
                SourceUUID     []string `json:"source_uuid"`
                Clusters       []string `json:"clusters"`
                Infrastructure struct {
                    Raw struct {
                        Value float64 `json:"value"`
                        Units string  `json:"units"`
                    } `json:"raw"`
                    Markup struct {
                        Value float64 `json:"value"`
                        Units string  `json:"units"`
                    } `json:"markup"`
                    Usage struct {
                        Value float64 `json:"value"`
                        Units string  `json:"units"`
                    } `json:"usage"`
                    Distributed struct {
                        Value float64 `json:"value"`
                        Units string  `json:"units"`
                    } `json:"distributed"`
                    Total struct {
                        Value float64 `json:"value"`
                        Units string  `json:"units"`
                    } `json:"total"`
                } `json:"infrastructure"`
                Supplementary struct {
                    Raw struct {
                        Value float64 `json:"value"`
                        Units string  `json:"units"`
                    } `json:"raw"`
                    Markup struct {
                        Value float64 `json:"value"`
                        Units string  `json:"units"`
                    } `json:"markup"`
                    Usage struct {
                        Value float64 `json:"value"`
                        Units string  `json:"units"`
                    } `json:"usage"`
                    Distributed struct {
                        Value float64 `json:"value"`
                        Units string  `json:"units"`
                    } `json:"distributed"`
                    Total struct {
                        Value float64 `json:"value"`
                        Units string  `json:"units"`
                    } `json:"total"`
                } `json:"supplementary"`
                Cost struct {
                    Raw struct {
                        Value float64 `json:"value"`
                        Units string  `json:"units"`
                    } `json:"raw"`
                    Markup struct {
                        Value float64 `json:"value"`
                        Units string  `json:"units"`
                    } `json:"markup"`
                    Usage struct {
                        Value float64 `json:"value"`
                        Units string  `json:"units"`
                    } `json:"usage"`
                    Distributed struct {
                        Value float64 `json:"value"`
                        Units string  `json:"units"`
                    } `json:"distributed"`
                    Total struct {
                        Value float64 `json:"value"`
                        Units string  `json:"units"`
                    } `json:"total"`
                } `json:"cost"`
            } `json:"values"`
        } `json:"projects"`
    } `json:"data"`
}

And here is a snippet of the output I am getting when running the program. As you can see, the Date, Project, and inner loop (CPU metrics) repeats itself, while the outer loop (Memory metrics) runs:

I'm looking for an output where I have one line per project (Month, Project, CPU metrics, Memory metrics)

Month, Project, CPU Request(Core hours), CPU Usage(Core hours) Memory Request(mBytes), Memory Usage(mBytes)
2022-12, amq-demo-streams, 0.0, 34.0, 0.0, 4353.2
2022-12, amq-demo-streams, 0.0, 34.0, 1115.6, 1081.4
2022-12, amq-demo-streams, 0.0, 34.0, 0.0, 10675.9
2022-12, amq-demo-streams, 0.0, 34.0, 100.9, 284.0
2022-12, amq-demo-streams, 0.0, 34.0, 0.0, 70064.5
2022-12, amq-demo-streams, 0.0, 34.0, 773088.9, 427757.8
2022-12, amq-demo-streams, 0.0, 34.0, 9440.0, 11610.3
2022-12, amq-demo-streams, 0.0, 34.0, 9471.3, 11696.9
2022-12, amq-demo-streams, 0.0, 34.0, 0.0, 2455.2
2022-12, amq-demo-streams, 0.0, 34.0, 0.0, 3.3
2022-12, amq-demo-streams, 0.0, 34.0, 0.0, 0.0
2022-12, amq-demo-streams, 0.0, 34.0, -0.3, 0.0
2022-12, amq-demo-streams, 0.0, 34.0, 3785.0, 6610.4
2022-12, amq-demo-streams, 0.0, 34.0, 252.3, 1007.8
2022-12, amq-demo-streams, 0.0, 34.0, 757.0, 883.0
2022-12, amq-demo-streams, 0.0, 34.0, 1009.4, 1613.4
2022-12, amq-demo-streams, 0.0, 34.0, 378.5, 413.5
2022-12, amq-demo-streams, 0.0, 34.0, 908.4, 2856.8
2022-12, amq-demo-streams, 0.0, 34.0, 252.3, 248.7
2022-12, amq-demo-streams, 0.0, 34.0, 66873.8, 21035.3
2022-12, amq-demo-streams, 0.0, 34.0, 353.3, 611.9
2022-12, amq-demo-streams, 0.0, 34.0, 10203.6, 12418.3
2022-12, amq-demo-streams, 0.0, 34.0, 504.7, 398.3
2022-12, amq-demo-streams, 0.0, 34.0, 1135.5, 2248.5
2022-12, amq-demo-streams, 0.0, 34.0, 252.3, 610.6
2022-12, amq-demo-streams, 0.0, 34.0, 252.3, 370.6

CodePudding user response:

I allowed myself to use simpler struct definition. You can always adapt this code to your structs.

type Cpu struct {
    Project string
    Data    []Data
}

type Memory struct {
    Project string
    Data    []Data
}

type Data struct {
    Date     string
    Projects []Project
}

type Project struct {
    Project string
    Values  []struct {
        Request float64
        Value   float64
    }
}

func CSVOutput(cpu Cpu, mem Memory) error {
    // Returns an error if cpu & memory's data are the same length
    if len(cpu.Data) != len(mem.Data) {
        return fmt.Errorf("cpu.Data and mem.Data don't have the same length")
    }

    // Printing CSV file header
    fmt.Println("Month, Project, CPU Request(Core hours), CPU Usage(Core hours) Memory Request(mBytes), Memory Usage(mBytes)")
    for i := range cpu.Data {
        cpuData := cpu.Data[i]
        memData := mem.Data[i]

        // Using the format from Errorf to add context to the error
        if len(cpuData.Projects) != len(memData.Projects) {
            return fmt.Errorf("cpu.Data[%d].Projects and mem.Data[%d].Projects don't have the same length", i, i)
        }

        for j := range cpuData.Projects {
            cpuProject := cpuData.Projects[j]
            memProject := memData.Projects[j]

            if len(cpuProject.Values) != len(memProject.Values) {
                return fmt.Errorf("cpu.Data[%d].Projects[%d].Values and mem.Data[%d].Projects[%d].Values don't have the same length", i, j, i, j)
            }

            name := cpuProject.Project
            date := cpuData.Date

            // Continue if the cpu project concerns openshift
            if strings.Contains(name, "openshift") {
                continue
            }

            for k := range cpuProject.Values {
                cpuValue := cpuProject.Values[k]
                memValue := memProject.Values[k]

                fmt.Printf("%s, %s, %.1f, %.1f, %.1f, %.1f", date, name, cpuValue.Request, cpuValue.Value, memValue.Request, memValue.Value)
            }
        }
    }

    return nil
}

This code only works if you receive as much cpu's data as memory's data.
If it isn't the case, you will have to find a way to link a certain cpu's data to its memory equivalent.
This issue can be furthermore discussed if you think the situation might show up.

  • Related