Reputation: 113
I want to write a generic function func GetVal(map[interface{}]interface{}, key interface{}) interface{}
. This will take a map and a key to search for and return either the value or nil.
The map can have any data type and can go to any level of nesting. For example,
var s1 = "S1"
var s2 = "S2"
var s3 = s1 + "==" + s2 + "==S3"
var s4 = s3 + "==S4"
var nestedMap = map[interface{}]interface{}{
"data": map[interface{}]interface{}{
"TEST_KEY": "1234353453",
"metadata": map[interface{}]interface{}{
"created_time": "2022-08-06",
},
"custom_metadata": map[interface{}][]interface{}{
"destroyed": []interface{}{
&s1,
map[string]interface{}{
"auth": []interface{}{
"val3", "val4", "val45",
},
},
},
},
&s2: &[]*string{
&s1, &s2,
},
&s1: &[]int{
10, 20, 233,
},
123: &s3,
},
s3: []interface{}{
[]interface{}{
map[string]*string{
s4: &s4,
},
},
},
}
Expected return values
GetVal(nestedMap, "metadata")
should return {"created_time": "2022-08-06"}
GetVal(nestedMap, "destroyed")
should return
{ &s1,
map[string]interface{}{
"auth": []interface{}{
"val3", "val4", "val45",
},
},
}
Is there a way to do it without an external library?
This question looks similar to Accessing Nested Map of Type map[string]interface{} in Golang but in my case the fields are not limited or always same
Upvotes: 1
Views: 1742
Reputation: 166
// Supports getting value by path: i.e. k2.nestedK3.superNestedK1
// Thanks Lukasz Szymik for his answer, which inspired me to implement this functionality based on his code.
func GetValueByPathFromMap(data map[string]any, key string, passedKey string) (result any, found bool) {
keyAndPath := strings.SplitN(key, ".", 2)
currentKey := keyAndPath[0]
if passedKey != "" {
passedKey = passedKey + "." + currentKey
} else {
passedKey = currentKey
}
if _, isKeyExistInData := data[currentKey]; !isKeyExistInData {
logrus.Warnf("[W] key path { %s } not found", passedKey)
return
} else {
if len(keyAndPath) > 1 {
remainingPath := keyAndPath[1]
switch data[currentKey].(type) {
case map[string]any:
if result, found = GetValueByPathFromMap(data[currentKey].(map[string]any), remainingPath, passedKey); found {
return
}
}
} else {
return data[currentKey], true
}
}
return nil, false
}
Upvotes: 0
Reputation: 21
The question is kind of cryptic because the example is overcomplicated. If you want to get knowledge regarding the recurrent functions, you should start with something simpler like:
var nestedMap = map[string]any{
"k1": "v1",
"k2": map[string]any{
"nestedK1": "nestedV1",
"nestedK2": "nestedV2",
"nestedK3": map[string]any{
"superNestedK1" : "FOUND!!!",
},
},}
Otherwise, an explanation will be hard.
Then you can work on functions like:
func GetVal(data map[string]any, key string) (result any, found bool) {
for k, v := range data {
if k == key {
return v, true
} else {
switch v.(type) {
case map[string]any:
if result, found = GetVal(v.(map[string]any), key); found {
return
}
}
}
}
return nil, false}
Later you can think about adding support for fancy stuff like map[interface{}][]interface{}
However, if you really need such complicated structure, I am not sure if the whole design of application is ok.
Maybe you should also think about adding searching for a full path inside the map k2.nestedK3.superNestedK1
. It will remove ambiguity.
Upvotes: 2