Sudheej
Sudheej

Reputation: 2019

Golang iterate over map of interfaces

I am trying to iterate over a map of interfaces in golang, it has the below structure, I am able to use for loop to iterate to a single level but couldn't go deep to get values of the interface.

Yaml

steps:
  execute:
  - mvn : 1.9.3
    goals: 'clean install'
    concurrent: false
  - mvn : 1.9.3
    goals: 'dependency-check:check'
    concurrent: false

Go

// reading a yaml file 
// trying to use "gopkg.in/yaml.v2" package

data, err := ioutil.ReadFile(fileName)
m := make(map[interface{}]interface{})
err = yaml.Unmarshal([]byte(data), &m)
    for k, v := range m {
       // Trying to explore the data here
        fmt.Println("k:", k, "v:", v)

    }

Output of

fmt.Printf("--- m:\n%v\n\n", m)

looks like below

map[steps:map[execute:[map[concurrent:false goals:clean install mvn:1.9.3] map[concurrent:false goals:dependency-check:check mvn:1.9.3]]]]

My attempt

for k, v := range m {
    fmt.Println("k:", k, "v:", v)

}

Upvotes: 0

Views: 4500

Answers (3)

volker
volker

Reputation: 31

Assuming that you have a tree of map[interface{}]interface{} and []interface{}, use the following code to walk the tree:

func walk(v interface{}) {
    switch v := v.(type) {
    case []interface{}:
        for i, v := range v {
            fmt.Println("index:", i)
            walk(v)
        }
    case map[interface{}]interface{}:
        for k, v := range v {
            fmt.Println("key:", k)
            walk(v)
        }
    default:
        fmt.Println(v)
    }
}

This code recurses down the structure and uses type switches to find the slices and maps.

Use it like this:

data, err := ioutil.ReadFile(fileName)
var m map[interface{}]interface{}
err = yaml.Unmarshal([]byte(data), &m)
walk(m)

Run it on the playground

Upvotes: 3

mri1939
mri1939

Reputation: 306

To access the Execute object and iterate over it you have to do a lot of type assertions like the answer given by @vooker.

Actually it's not recommended to use map[interface{}]interface{} for unmarshaling process. It should be unmarshalled to a concrete type as a struct.

From the YAML structure, it could be translated as a struct type like this:


type data struct {
    Steps struct {
        Execute []execute `yaml:"execute"`
    } `yaml:"steps"`
}

type execute struct {
    MVN        string `yaml:"mvn"`
    Goals      string `yaml:"goals"`
    Concurrent bool   `yaml:"concurrent"`
}

And from the structure, it is easier to access the unmarshalled data from the YAML. For example, if you want to iterate over the "execute" object you could do something like this:

    var result data
    yaml.Unmarshal([]byte(str), &result)
    fmt.Println(result) 

    // iterate over the slice of object
    for _, e := range result.Steps.Execute {
        fmt.Println()
        fmt.Println("mvn:", e.MVN)
        fmt.Println("goals:", e.Goals)
        fmt.Println("concurrent:", e.Concurrent)
    }

Try it on the playground

Upvotes: 1

Corey Ogburn
Corey Ogburn

Reputation: 24717

I don't know what you mean with that "value of m" as that doesn't look like any format I've seen in Go so I'll talk to a few situations: When the interfaces are probably types you know what they are vs when the interfaces could be anything and you aren't sure.

If there are only a couple of types that you know went into the map, you can do a type switch and handle each type individually. This would give you the opportunity to reference subfields and print them as well. If you always print the same info for the same type of objects, you could look into adding a String() string function to your structs which will make them implement the Stringer interface and then you can print the object and your String() func will get called even if it's boxed as an interface.

If you're working with a library that's filling the map, or there's simply too big a diversity of the types in the map for a type switch to be reasonable, then you'll either want a generic solution such as a library like spew or a custom solution written with reflection.

Upvotes: 1

Related Questions