Skip to content

Instantly share code, notes, and snippets.

@adambabik
Created December 6, 2016 11:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save adambabik/bc80eefa3740af751018e7755f52c220 to your computer and use it in GitHub Desktop.
Save adambabik/bc80eefa3740af751018e7755f52c220 to your computer and use it in GitHub Desktop.
Go Maps Merge
package main
import "reflect"
// CopyOmmitedKeys copies only keys from the source which do not exist in dest.
func CopyOmmitedKeys(dest, source map[string]interface{}) {
leftV, rightV := reflect.ValueOf(dest), reflect.ValueOf(source)
leftKeysV, rightKeysV := leftV.MapKeys(), rightV.MapKeys()
leftKeysSet := map[string]bool{}
for _, leftKeyV := range leftKeysV {
leftKeysSet[leftKeyV.Interface().(string)] = true
}
for _, rightKeyV := range rightKeysV {
rightKeyStr := rightKeyV.Interface().(string)
if _, ok := leftKeysSet[rightKeyStr]; ok {
continue
}
dest[rightKeyStr] = rightV.MapIndex(rightKeyV).Interface()
}
}
// MergeMaps merges sources into dest by comparing keys.
func MergeMaps(dest, source map[string]interface{}) map[string]interface{} {
result := map[string]interface{}{}
resultV := reflect.ValueOf(result)
leftV, rightV := reflect.ValueOf(dest), reflect.ValueOf(source)
leftKeysV := leftV.MapKeys()
// goon.Dump(dest)
// goon.Dump(source)
for _, leftKeyV := range leftKeysV {
leftValV := leftV.MapIndex(leftKeyV)
sourceValV := rightV.MapIndex(leftKeyV)
if !sourceValV.IsValid() {
resultV.SetMapIndex(leftKeyV, leftValV)
continue
}
// fmt.Printf("=== key %s\n", leftKeyV.Interface().(string))
// fmt.Printf("=== kind %s val %s\n", leftValV.Elem().Kind(), leftValV.Interface())
// fmt.Printf("=== kind %s val %s\n", sourceValV.Elem().Kind(), sourceValV.Interface())
kind := leftValV.Elem().Kind()
if kind == reflect.Map && kind == sourceValV.Elem().Kind() {
resultV.SetMapIndex(leftKeyV, reflect.ValueOf(MergeMaps(
leftValV.Interface().(map[string]interface{}),
sourceValV.Interface().(map[string]interface{}),
)))
} else if kind == reflect.Slice && kind == sourceValV.Elem().Kind() {
// fmt.Printf("==== %s %s \n", kind, sourceValV.Elem().Kind())
// goon.Dump(leftValV.Interface())
// goon.Dump(sourceValV.Interface())
resultV.SetMapIndex(
leftKeyV,
reflect.AppendSlice(leftValV.Elem(), sourceValV.Elem()))
} else {
resultV.SetMapIndex(leftKeyV, sourceValV)
}
}
CopyOmmitedKeys(result, source)
return result
}
package main
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestMergeOneLevelDeepMaps(t *testing.T) {
a := map[string]interface{}{
"key1": 1,
}
b := map[string]interface{}{
"key1": 2,
}
c := MergeMaps(a, b)
assert.Equal(t, map[string]interface{}{
"key1": 2,
}, c)
}
func TestMergeTwoLevelDeepMaps(t *testing.T) {
a := map[string]interface{}{
"key1": map[string]interface{}{
"key11": 1,
},
}
b := map[string]interface{}{
"key1": map[string]interface{}{
"key11": 2,
},
}
c := MergeMaps(a, b)
assert.Equal(t, map[string]interface{}{
"key1": map[string]interface{}{
"key11": 2,
},
}, c)
}
func TestMergeUnevenMaps(t *testing.T) {
a := map[string]interface{}{
"key1": map[string]interface{}{
"key11": 1,
},
}
b := map[string]interface{}{
"key1": 2,
}
c := MergeMaps(a, b)
assert.Equal(t, map[string]interface{}{
"key1": 2,
}, c)
c = MergeMaps(b, a)
assert.Equal(t, map[string]interface{}{
"key1": map[string]interface{}{
"key11": 1,
},
}, c)
}
func TestMergeMapsWithSlices(t *testing.T) {
a := map[string]interface{}{
"key1": map[string]interface{}{
"key11": []int{1, 2},
},
}
b := map[string]interface{}{
"key1": map[string]interface{}{
"key11": []int{3, 4},
},
}
c := MergeMaps(a, b)
assert.Equal(t, map[string]interface{}{
"key1": map[string]interface{}{
"key11": []int{1, 2, 3, 4},
},
}, c)
}
func TestMergeWithAdditionalKeys(t *testing.T) {
a := map[string]interface{}{
"key1": map[string]interface{}{
"key11": []int{1, 2},
"key12": 3,
},
"key2": 4,
}
b := map[string]interface{}{
"key1": map[string]interface{}{
"key11": []int{3, 4},
},
}
c := MergeMaps(a, b)
assert.Equal(t, map[string]interface{}{
"key1": map[string]interface{}{
"key11": []int{1, 2, 3, 4},
"key12": 3,
},
"key2": 4,
}, c)
c = MergeMaps(b, a)
assert.Equal(t, map[string]interface{}{
"key1": map[string]interface{}{
"key11": []int{3, 4, 1, 2},
"key12": 3,
},
"key2": 4,
}, c)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment