Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add MergePathAndValueIntoMap #49

Merged
merged 4 commits into from
Mar 29, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions helpers/misc.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"math"
"reflect"
"regexp"
"strings"
"time"
)

Expand Down Expand Up @@ -146,3 +147,28 @@ func MergeNonZero[T any](original T, overrides T) T {
}
return originalValue.Elem().Interface().(T)
}

// MergePathAndValueIntoMap takes a path in dot notation as a string and a value (also as a string for simplicity),
// then merges this into the provided map. The value can be any type.
func MergePathAndValueIntoMap(m map[string]any, path string, value any) error {
lucasrod16 marked this conversation as resolved.
Show resolved Hide resolved
pathParts := strings.Split(path, ".")
current := m
for i, part := range pathParts {
if i == len(pathParts)-1 {
// Set the value at the last key in the path.
current[part] = value
} else {
if _, exists := current[part]; !exists {
// If the part does not exist, create a new map for it.
current[part] = make(map[string]any)
}

nextMap, ok := current[part].(map[string]any)
if !ok {
return fmt.Errorf("conflict at '%s', expected map but got %T", strings.Join(pathParts[:i+1], "."), current[part])
naveensrinivasan marked this conversation as resolved.
Show resolved Hide resolved
}
current = nextMap
}
}
return nil
}
87 changes: 87 additions & 0 deletions helpers/misc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package helpers

import (
"errors"
"reflect"
"strings"
"testing"

Expand Down Expand Up @@ -199,3 +200,89 @@ func (suite *TestMiscSuite) TestBoolPtr() {
func TestMisc(t *testing.T) {
suite.Run(t, new(TestMiscSuite))
}

func TestMergePathAndValueIntoMap(t *testing.T) {
naveensrinivasan marked this conversation as resolved.
Show resolved Hide resolved
type args struct {
m map[string]interface{}
path string
value interface{}
}
tests := []struct {
name string
args args
wantErr bool
want map[string]any
}{
{
name: "nested map creation",
args: args{m: make(map[string]interface{}), path: "a.b.c", value: "hello"},
wantErr: false,
want: map[string]any{
"a": map[string]any{
"b": map[string]any{
"c": "hello",
},
},
},
},
{
name: "overwrite existing value",
args: args{m: map[string]interface{}{"a": map[string]any{"b": map[string]any{"c": "initial"}}},
path: "a.b.c", value: "updated"},
wantErr: false,
want: map[string]any{
"a": map[string]any{
"b": map[string]any{
"c": "updated",
},
},
},
},
{
name: "deeply nested map creation",
args: args{m: make(map[string]interface{}), path: "a.b.c.d.e.f", value: 42},
wantErr: false,
want: map[string]any{
"a": map[string]any{
"b": map[string]any{
"c": map[string]any{
"d": map[string]any{
"e": map[string]any{
"f": 42,
},
},
},
},
},
},
},
{
name: "empty path",
args: args{m: make(map[string]interface{}), path: "", value: "root level"},
wantErr: false,
want: map[string]any{
"": "root level",
},
},
{
name: "root level value",
args: args{m: make(map[string]interface{}), path: "root", value: "root value"},
wantErr: false,
want: map[string]any{
"root": "root value",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := MergePathAndValueIntoMap(tt.args.m, tt.args.path, tt.args.value)
if (err != nil) != tt.wantErr {
t.Errorf("MergePathAndValueIntoMap() error = %v, wantErr %v", err, tt.wantErr)
}

if !reflect.DeepEqual(tt.args.m, tt.want) {
t.Errorf("Map structure mismatch: got %v, want %v", tt.args.m, tt.want)
}
naveensrinivasan marked this conversation as resolved.
Show resolved Hide resolved
})
}
}
Loading