英文:
recursively flatten a map golang
问题
使用以下的JSON数据,我正在尝试将其展开以便于访问。
例如,有2个资源,这个结构中可以有任意数量的资源:
"config": {
"type": "r1",
"properties": {
"p1": "10",
"p2": "10"
},
"connected": [
{
"type": "r3",
"properties": {
"p1": "10",
"p2": "10"
},
"connected": [
{}
},
],
}
自定义展开逻辑:
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) {
nnv, _ := nv.(map[string]interface{})
_, ok := nnv["connectedTo"]
if !ok {
nnv["connectedTo"] = key
}
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
}
期望的输出有些波动,有些运行时我得到了所需的输出,有些执行中"connectedTo"属性是空的。
{
"r1": {
"p1": "10",
"p2": "10"
},
"r3" : {
"connectedTo": "r1",
"p1": "10",
"p2": "10"
},
}
我认为执行不是顺序执行的。如果我错了,请纠正我。
英文:
Using the below json Im trying to flatten it for ease of accessibility.
Example having 2 resource there can be n number of in this structure
"config": {
"type": "r1",
"properties": {
"p1": "10",
"p2": "10"
},
"connected": [
{
"type": "r3",
"properties": {
"p1": "10",
"p2": "10"
},
"connected": [
{}
},
],
}
Custom flatterning logic
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) {
nnv, _ := nv.(map[string]interface{})
_, ok := nnv["connectedTo"]
if !ok {
nnv["connectedTo"] = key
}
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 desired output is kinda fluctuationg, some runs I get the output what I require and some executions "connectedTo" attribute is empty.
{
"r1": {
"p1": "10",
"p2": "10"
},
"r3" : {
"connectedTo": "r1",
"p1": "10",
"p2": "10"
},
}
I think the executions are not sequential. Please do correct me if Im wrong.
答案1
得分: 2
迭代地图的顺序是随机的。详细信息请参见https://stackoverflow.com/questions/55925822/why-are-iterations-over-maps-random/55925880#55925880和https://stackoverflow.com/questions/28930416/how-to-iterate-maps-in-insertion-order/28931555#28931555
现在看看你的循环:
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) {
nnv, _ := nv.(map[string]interface{})
_, ok := nnv["connectedTo"]
if !ok {
nnv["connectedTo"] = key
}
kvs[nk] = nv
}
}
}
default:
for nk, nv := range keyValuePairs(v) {
kvs[nk] = nv
}
}
}
你在“connected”分支中使用了“key”变量,但是“type”和“connected”的到达顺序是不确定的(随机的)。
“type”分支是设置“key”的地方,但是如果先到达“connected”,那么“key”将为空。
你不能依赖于地图的迭代顺序。
一个简单的修复方法是在循环之前首先获取与“type”关联的值,并将其赋给“key”(你在“connected”分支中使用),在循环之前。
例如:
key, _ := mp["type"].(string)
value := mp["properties"]
// 现在处理其他属性:
for k, v := range mp {
switch k {
case "type", "properties": // 已经处理过了
case "connected":
if collection, ok := v.([]interface{}); ok {
for _, c := range collection {
for nk, nv := range keyValuePairs(c) {
nnv, _ := nv.(map[string]interface{})
_, ok := nnv["connectedTo"]
if !ok {
nnv["connectedTo"] = key
}
kvs[nk] = nv
}
}
}
default:
for nk, nv := range keyValuePairs(v) {
kvs[nk] = nv
}
}
}
英文:
Iteration order over a map is random. For details, see https://stackoverflow.com/questions/55925822/why-are-iterations-over-maps-random/55925880#55925880 and https://stackoverflow.com/questions/28930416/how-to-iterate-maps-in-insertion-order/28931555#28931555
Now look at your loop:
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) {
nnv, _ := nv.(map[string]interface{})
_, ok := nnv["connectedTo"]
if !ok {
nnv["connectedTo"] = key
}
kvs[nk] = nv
}
}
}
default:
for nk, nv := range keyValuePairs(v) {
kvs[nk] = nv
}
}
}
You are using the key
variable in the "connected"
branch, but the order in which "type"
and "connected"
will be reached is non-deterministic (random).
The "type"
branch is what sets key
, but if "connected"
is reached first, key
will be empty.
You must not rely on map iteration order.
An easy fix is to first get the value associated with"type"
first and assign it to key
(which you use in the "connected"
branch), before the loop.
For example:
key, _ := mp["type"].(string)
value := mp["properties"]
// Now process other properties:
for k, v := range mp {
switch k {
case "type", "properties": // Already handled
case "connected":
if collection, ok := v.([]interface{}); ok {
for _, c := range collection {
for nk, nv := range keyValuePairs(c) {
nnv, _ := nv.(map[string]interface{})
_, ok := nnv["connectedTo"]
if !ok {
nnv["connectedTo"] = key
}
kvs[nk] = nv
}
}
}
default:
for nk, nv := range keyValuePairs(v) {
kvs[nk] = nv
}
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论