如何高效地自定义展平一个映射(map)?

huangapple go评论85阅读模式
英文:

How to efficiently custom flatten a map

问题

根据传递的resourcesToCreate参数,我必须相应地创建它们以创建流程。

但现在的问题是,在分配funcInput时,json实际上是根据创建的流程嵌套的,属性可以出现在任何位置,所以我不能硬编码。

例如:

"config": {
    "type": "func1",
    "config": {
        "param1": "10",
        "param2": "10"
    },
    "connected": [
        {
            "type": "func3",
            "config": {
              "param1": "10",
              "param2": "10"
            },
            "connected": [
                {}
        },
    ],
}

所以我想,如果我将其展平,我可以不考虑流程而引用它:

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

目前的代码将json展平,但在获取所需输出时遇到了问题:

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
		}
	}
}
英文:

So based on the resourcesToCreate parameter passed I have to create them accordingly to create the flow

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 
&quot;config&quot;: {
    &quot;type&quot;: &quot;func1&quot;,
    &quot;config&quot;: {
        &quot;param1&quot;: &quot;10&quot;,
        &quot;param2&quot;: &quot;10&quot;
    },
    &quot;connected&quot;: [
        {
            &quot;type&quot;: &quot;func3&quot;,
            &quot;config&quot;: {
              &quot;param1&quot;: &quot;10&quot;,
              &quot;param2&quot;: &quot;10&quot;
            },
            &quot;connected&quot;: [
                {}
        },
    ],
}

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

{
&quot;func1&quot;: {
        &quot;param1&quot;: &quot;10&quot;,
        &quot;param2&quot;: &quot;10&quot;
    },
&quot;func3&quot; : {
        &quot;param1&quot;: &quot;10&quot;,
        &quot;param2&quot;: &quot;10&quot;
    },
}

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) &gt; 0 {
		prefix += &quot;.&quot;
	}
	for k, v := range src {
		switch child := v.(type) {
		case map[string]interface{}:
			Flatten2(prefix+k, child, dest)
		case []interface{}:
			for i := 0; i &lt; len(child); i++ {
				dest[prefix+k+&quot;.&quot;+strconv.Itoa(i)] = child[i]
			}
		default:
			dest[prefix+k] = v
		}
	}
}
``

</details>


# 答案1
**得分**: 1

这个扁平化操作更像是提取值。通用的方法不可能存在,只能依赖于JSON的键,比如`type`、`properties`和`connected`,来高效地提取`func`和`param`的值。你的扁平化函数应该像这样:

```go
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
}

相同的代码示例输入:Go Playground

为了更好地理解代码片段,请查看这个gist,在那里我实际上是将一个JSON扁平化为最后的键值对。

英文:

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 &quot;type&quot;:
					key = v.(string)
				case &quot;properties&quot;:
					value = v
				case &quot;connected&quot;:
					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 != &quot;&quot; {
				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.

huangapple
  • 本文由 发表于 2022年5月21日 14:11:12
  • 转载请务必保留本文链接:https://go.coder-hub.com/72327111.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定