How to efficiently custom flatten a map

Issue

So based on the resourcesToCreate parameter passed I have to create them accordingly to create the flow and I have written below logic for it

resourcesToCreate := "func1-func3"

func createFlow(resourcesToCreate) {
    resources := strings.Split(resourcesToCreate, "-")

    for _, resource := range resources {
        switch resource {
        case "func1":
            func1Input := func1Input{}
            func1(func1Input)

        case "func2":
            func2Input := func2Input{}
            func2(func2Input)

        case "func3":
            func3Input := func3Input{}
            func3(func3Input)
        }
      }
}

But now the problem is while assigning the funcInput the json is actually nested based on the flow it creates and attributes can be present anywhere so I cant harcode.

Example 1: Contains on one resource
"config": {
        "type": "func3",
        "properties": {
            "param1": "10",
            "param2": "10"
        }
    }

Example 2: Contains 2 resources connected to each other
"config": {
    "type": "func1",
    "properties": {
        "param1": "10",
        "param2": "10"
    },
    "connected": [
        {
            "type": "func3",
            "properties": {
              "param1": "10",
              "param2": "10"
            },
            "connected": [
                {}
        },
    ],
}

So was thinking it I flatten it like this I can refer irrespective of the flow

{
"func1": {
        "param1": "10",
        "param2": "10"
    },
"func3" : {
        "param1": "10",
        "param2": "10"
    },
}

So now I can easily hardcode the path

func3Config := config["func3"]
func3Input := func3Input{
     param1: func3Config["param1"]
     param2: func3Config["param2"]
}

So the code currently fattens the json but having trouble getting the desired output

func Flatten(prefix string, src map[string]interface{}, dest map[string]interface{}) {
    if len(prefix) > 0 {
        prefix += "."
    }
    for k, v := range src {
        switch child := v.(type) {
        case map[string]interface{}:
            Flatten2(prefix+k, child, dest)
        case []interface{}:
            for i := 0; i < len(child); i++ {
                dest[prefix+k+"."+strconv.Itoa(i)] = child[i]
            }
        default:
            dest[prefix+k] = v
        }
    }
}
``

Solution

This flattening is more like extracting values. Generic way is not possible but to depend on the JSON keys like type, properties and connected to efficiently extract the func and param values. Your flatten function should be like:

func keyValuePairs(m interface{}) map[string]interface{} {
    kvs := make(map[string]interface{})
    if reflect.ValueOf(m).Kind() == reflect.Map {
        mp, ok := m.(map[string]interface{})
        if ok {
            var key string
            var value interface{}
            for k, v := range mp {
                switch k {
                case "type":
                    key = v.(string)
                case "properties":
                    value = v
                case "connected":
                    if collection, ok := v.([]interface{}); ok {
                        for _, c := range collection {
                            for nk, nv := range keyValuePairs(c) {
                                kvs[nk] = nv
                            }
                        }
                    }
                default:
                    for nk, nv := range keyValuePairs(v) {
                        kvs[nk] = nv
                    }
                }
            }
            if key != "" {
                kvs[key] = value
            }
        } else {
            for k, v := range m.(map[string]interface{}) {
                kvs[k] = v
            }
        }
    }
    return kvs
}

The same code with example inputs: Go Playground

For better understanding the code snippet, check this gist where I was actually flattening a JSON to very last key-value pairs.

Answered By – Vague Coder

Answer Checked By – Gilberto Lyons (GoLangFix Admin)

Leave a Reply

Your email address will not be published.