Home > Software design >  How to convert a slice of maps to a slice of structs with different properties
How to convert a slice of maps to a slice of structs with different properties

Time:12-05

I am working with an api and I need to pass it a slice of structs. I have a slice of maps so I need to convert it to a slice of structs.

package main

import "fmt"

func main() {
    a := []map[string]interface{}{}
    b := make(map[string]interface{})
    c := make(map[string]interface{})
    
    b["Prop1"] = "Foo"
    b["Prop2"] = "Bar"
    a = append(a, b)

    c["Prop3"] = "Baz"
    c["Prop4"] = "Foobar"
    a = append(a, c)

    fmt.Println(a)
}

[map[Prop1:Foo Prop2:Bar] map[Prop3:Baz Prop4:Foobar]]

so in this example, I have the slice of maps a, which contains b and c which are maps of strings with different keys.

I'm looking to convert a to a slice of structs where the first element is a struct with Prop1 and Prop2 as properties, and where the second element is a struct with Prop3 and Prop4 as properties.

Is this possible?

I've looked at https://github.com/mitchellh/mapstructure but I wasn't able to get it working for my use case. I've looked at this answer: https://stackoverflow.com/a/26746461/3390419

which explains how to use the library:

mapstructure.Decode(myData, &result)

however this seems to assume that the struct of which result is an instance is predefined, whereas in my case the structure is dynamic.

CodePudding user response:

What you can do is to first loop over each map individually, using the key-value pairs of each map you construct a corresponding slice of reflect.StructField values. Once you have such a slice ready you can pass it to reflect.StructOf, that will return a reflect.Type value that represents the dynamic struct type, you can then pass that to reflect.New to create a reflect.Value which will represent an instance of the dynamic struct (actually pointer to the struct).

E.g.

var result []any
for _, m := range a {
    fields := make([]reflect.StructField, 0, len(m))

    for k, v := range m {
        f := reflect.StructField{
            Name: k,
            Type: reflect.TypeOf(v), // allow for other types, not just strings
        }
        fields = append(fields, f)
    }

    st := reflect.StructOf(fields) // new struct type
    sv := reflect.New(st)          // new struct value

    for k, v := range m {
        sv.Elem(). // dereference struct pointer
                FieldByName(k).         // get the relevant field
                Set(reflect.ValueOf(v)) // set the value of the field
    }

    result = append(result, sv.Interface())
}

https://go.dev/play/p/NzHQzKwhwLH

  •  Tags:  
  • go
  • Related