Python like nested dictionary in GO Lang

Issue

Disclaimer: I recently started learning Go. so if this question seems to you guys very naive, please pardon me.

I’m basically trying to replicate the Python-like nested dictionary behaviour in Go using the map.

here is a simple python function that I want to convert to Go

def generate_chart():

    outcome_chart = {}

    A_LIST = ['a1', 'a2', 'a3', 'a4', ....., 'a10']
    B_LIST = ['b1', 'b2', 'b3', 'b4', ....., 'b10']
    C_LIST = ['c1', 'c2', 'c3', 'c4', ....., 'c10']

    for a in A_LIST:
        for b in B_LIST:
            for c in C_LIST:
                outcome_chart[a] = outcome_chart.get(a) or {}
                outcome_chart[a][b] = outcome_chart[a].get(b) or {}
                result = "some string from external source, would be different on each request" 
                outcome_chart[a][b].update({c: result})

    return outcome_chart

the idea is simple, I have 3 lists of strings. and trying to create a map/chart for different combinations.
An example after this function run

{
   'a1': {'b1': {'c1': 'somestring'}},
   ...
   'a2': {'b1': {'c3': 'someotherstring'}},
   ...
   'a6': {'b7': {'c2': 'string'}},
   ...
 }

Now based on my very little knowledge of Go, I’ve come up with this piece of code but it’s not complete as i got stuck in the middle.

func generateChart() map[string]map[string]map[string]string {
    var result string
    var outcome_chart = map[string]map[string]map[string]string{}
    for _, a := range A_LIST {
        for _, b := range B_LIST {
            for _, c := range C_LIST {
                
                result = "some string from external source, would be different on each request" 

                if _, ok := outcome_chart[a]; ok {
                    if _, found := outcome_chart[a][b]; found {
                        if _, in := outcome_chart[a][b][c]; in {
                            outcome_chart[a][b][c] = result
                        } else {
                            outcome_chart[a][b] = map[string]string{c: result}
                        }
                    } else {
                        outcome_chart[a] = map[string]map[string]string{b: {c: result}}
                    }
                    outcome_chart[a] = # got stuck here, dont know if the previous lines are also correct.
                }
            }
        }
    }

    return outcome_chart

I might be doing it in a completely wrong way. There might be a solution using struct or/and interface but as I mentioned I’m still trying to expose myself to GO world. So all the feedback/suggestions/inputs are welcome.

PS: this was only 3 level nested so it was still scalable to write manually 3 level map but how can we achieve similar thing for more nesting level lets say 100s or 1000s or even infinite.

Solution

The equivalent in Go:

func generateChart() map[string]map[string]map[string]string {
    var outcome_chart = map[string]map[string]map[string]string{}
    for _, a := range A_LIST {
        for _, b := range B_LIST {
            for _, c := range C_LIST {
                if _, ok := outcome_chart[a]; !ok {
                    outcome_chart[a] = map[string]map[string]string{}
                }

                if _, ok := outcome_chart[a][b]; !ok {
                    outcome_chart[a][b] = map[string]string{}
                }

                result := "some string from external source, would be different on each request"
                outcome_chart[a][b][c] = result
            }
        }
    }
    return outcome_chart
}

A more economical approach to achieve the same:

func generateChart() map[string]map[string]map[string]string {
    var outcome_chart = map[string]map[string]map[string]string{}
    for _, a := range A_LIST {
        if _, ok := outcome_chart[a]; !ok {
            outcome_chart[a] = map[string]map[string]string{}
        }

        for _, b := range B_LIST {
            if _, ok := outcome_chart[a][b]; !ok {
                outcome_chart[a][b] = map[string]string{}
            }

            for _, c := range C_LIST {
                result := "some string from external source, would be different on each request"
                outcome_chart[a][b][c] = result
            }
        }
    }

    return outcome_chart
}

For something more nested you can use a declared recursive type sprinkled with some methods for convenience:

type Chart struct {
    M map[string]*Chart
    V string
}

func newChart() *Chart {
    return &Chart{M: make(map[string]*Chart)}
}

func (c *Chart) addIfEmpty(key string) *Chart {
    if _, ok := c.M[key]; !ok {
        c.M[key] = newChart()
    }
    return c.M[key]
}

func generateChart() *Chart {
    var chart = newChart()
    for _, a := range A_LIST {
        aChart := chart.addIfEmpty(a)
        for _, b := range B_LIST {
            bChart := aChart.addIfEmpty(b)
            for _, c := range C_LIST {
                result := "some string from external source, would be different on each request"
                bChart.addIfEmpty(c).V = result
            }
        }
    }
    return chart
}

Answered By – mkopriva

Answer Checked By – Marilyn (GoLangFix Volunteer)

Leave a Reply

Your email address will not be published.