Skip to content

Instantly share code, notes, and snippets.

@atombender
Created April 15, 2016 05:13
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save atombender/01a07926115a17d3a56145adc0402c02 to your computer and use it in GitHub Desktop.
Save atombender/01a07926115a17d3a56145adc0402c02 to your computer and use it in GitHub Desktop.
// SetKeyPath sets a value in an arbitrarily nested map, where the key is given as
// a path into the target map. For example, if the map is {a: {b: 1}}, then the key
// path ["a", "b"] sets the inner key.
func SetKeyPath(m interface{}, keyPath []string, newValue interface{}) error {
targetMap := reflect.ValueOf(m)
if targetMap.Kind() != reflect.Map {
return fmt.Errorf("Argument not a map: %#v", m)
}
length := len(keyPath)
if length == 0 {
return nil
}
key := reflect.ValueOf(keyPath[0])
if length == 1 {
targetMap.SetMapIndex(key, reflect.ValueOf(newValue))
return nil
}
v := reflect.ValueOf(unwrapReflectValue(targetMap.MapIndex(key)))
if v.Kind() != reflect.Map {
v = reflect.Indirect(reflect.MakeMap(targetMap.Type()))
targetMap.SetMapIndex(key, v)
}
return SetKeyPath(v.Interface(), keyPath[1:], newValue)
}
func unwrapReflectValue(v reflect.Value) interface{} {
if v.IsNil() {
return nil
}
if v.Kind() == reflect.Ptr {
v = v.Elem()
if v.IsNil() {
return nil
}
}
if v.Kind() == reflect.Interface {
v = v.Elem()
}
return v.Interface()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment