递归地展开一个映射(map)的 Golang 实现。

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

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

huangapple
  • 本文由 发表于 2022年6月2日 18:45:26
  • 转载请务必保留本文链接:https://go.coder-hub.com/72474936.html
匿名

发表评论

匿名网友

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

确定