Skip to content

Commit

Permalink
Merge pull request #515 from kellerza/freeform
Browse files Browse the repository at this point in the history
Merging maps
  • Loading branch information
hellt committed Jul 15, 2021
2 parents 34d8fb8 + 19e21de commit f388bf5
Show file tree
Hide file tree
Showing 2 changed files with 146 additions and 15 deletions.
67 changes: 52 additions & 15 deletions utils/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

package utils

import "reflect"

// convertEnvs convert env variables passed as a map to a list of them
func ConvertEnvs(m map[string]string) []string {
s := make([]string, 0, len(m))
Expand All @@ -13,27 +15,62 @@ func ConvertEnvs(m map[string]string) []string {
return s
}

// mergeStringMaps merges map m1 into m2 and return a resulting map as a new map
// maps that are passed for merging will not be changed
func MergeStringMaps(m1, m2 map[string]string) map[string]string {
if m1 == nil {
return m2
}
if m2 == nil {
return m1
func mapify(i interface{}) (map[string]interface{}, bool) {
value := reflect.ValueOf(i)
if value.Kind() == reflect.Map {
m := map[string]interface{}{}
for _, k := range value.MapKeys() {
m[k.String()] = value.MapIndex(k).Interface()
}
return m, true
}
// make a copy of a map
m := make(map[string]string)
for k, v := range m1 {
m[k] = v
return map[string]interface{}{}, false
}

// merge all dictionaries and return a new dictionary
// recursively if matching keys are both dictionaries
func MergeMaps(dicts ...map[string]interface{}) map[string]interface{} {
res := make(map[string]interface{})
for _, m := range dicts {
if m == nil {
continue
}
for k, v := range m {

if v0, ok := res[k]; ok {
// Recursive merging if res[k] exists (and both are dicts)
t0, ok0 := mapify(v0)
t1, ok1 := mapify(v)
if ok0 && ok1 {
res[k] = MergeMaps(t0, t1)
continue
}
}
res[k] = v
}
}
return res
}

for k, v := range m2 {
m[k] = v
// merge all string maps and return a new map
// maps that are passed for merging will not be changed
func MergeStringMaps(maps ...map[string]string) map[string]string {
res := make(map[string]string)
for _, m := range maps {
if m == nil {
continue
}
for k, v := range m {
res[k] = v
}
}
if len(res) == 0 {
return nil
}
return m
return res
}

// does a slice contain a string
func StringInSlice(slice []string, val string) (int, bool) {
for i, item := range slice {
if item == val {
Expand Down
94 changes: 94 additions & 0 deletions utils/env_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package utils

import (
"fmt"
"testing"

"github.com/google/go-cmp/cmp"
)

func expectMaps(t *testing.T, v map[string]interface{}, m ...map[string]interface{}) {
d := MergeMaps(m...)
if !cmp.Equal(d, v) {
t.Errorf("err %v, expected %v", d, v)
}
}

func TestMergeMaps(t *testing.T) {
MergeMaps(nil, nil)
d1 := map[string]interface{}{
"t": "1",
}
d2 := map[string]interface{}{
"t": "2",
"t2": "1",
}
expectMaps(t, d1, nil, d1)
expectMaps(t, d1, d1, d1)
expectMaps(t, d1, d1, nil)
expectMaps(t, d2, d1, d2)
expectMaps(t, map[string]interface{}{
"t": "1",
"t2": "1",
}, d2, d1)
}

func TestMergeMapsRecursive(t *testing.T) {
d0 := map[string]interface{}{
"a": "1",
}
d1 := map[string]interface{}{
"a": "11",
"b": "2",
}
r0 := map[string]interface{}{
"r": d0,
"r1": "1",
}
r1 := map[string]interface{}{
"r": d1,
"r2": "2",
}
r3 := map[string]interface{}{
"r": "00",
"r2": "0",
}

exp0 := map[string]interface{}{
"a": "1",
"b": "2",
}
exp1 := d1

// all simple vars... second overwrites
expectMaps(t, exp0, d1, d0)
expectMaps(t, exp1, d0, d1)

// r are both dicts... recursive on r... same inner result as the previous
expectMaps(t, map[string]interface{}{"r": exp0, "r1": "1", "r2": "2"}, r1, r0)
expectMaps(t, map[string]interface{}{"r": exp1, "r1": "1", "r2": "2"}, r0, r1)

// one is NOT a dict... second overwrites
expectMaps(t, map[string]interface{}{"r": "00", "r2": "0"}, r1, r3)
expectMaps(t, map[string]interface{}{"r": exp1, "r2": "2"}, r3, r1)
}

func TestMergeStringMaps(t *testing.T) {
d0 := map[string]string{
"a": "1",
}
d1 := map[string]string{
"a": "11",
"b": "2",
}

expect := func(m1, m2 map[string]string, v string) {
d := MergeStringMaps(m1, m2)
if fmt.Sprintf("%v", d) != v {
t.Errorf("err %v, expected %s", d, v)
}
}
// all simple vars... second overwrites
expect(d1, d0, "map[a:1 b:2]")
expect(d0, d1, "map[a:11 b:2]")
}

0 comments on commit f388bf5

Please sign in to comment.