Skip to content

Commit 1b37ff8

Browse files
leslie-qiwanqd
authored andcommitted
add prefix option to allow caller for flattern customization
Signed-off-by: Leslie Qi Wang <[email protected]>
1 parent bf87297 commit 1b37ff8

File tree

2 files changed

+125
-1
lines changed

2 files changed

+125
-1
lines changed

flat.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
// Options the flatten options.
1212
// By default: Delimiter = "."
1313
type Options struct {
14+
Prefix string
1415
Delimiter string
1516
Safe bool
1617
MaxDepth int
@@ -27,7 +28,7 @@ func Flatten(nested map[string]interface{}, opts *Options) (m map[string]interfa
2728
}
2829
}
2930

30-
m, err = flatten("", 0, nested, opts)
31+
m, err = flatten(opts.Prefix, 0, nested, opts)
3132

3233
return
3334
}
@@ -125,6 +126,9 @@ func unflatten(flat map[string]interface{}, opts *Options) (nested map[string]in
125126
func uf(k string, v interface{}, opts *Options) (n interface{}) {
126127
n = v
127128

129+
if opts.Prefix != "" {
130+
k = strings.TrimPrefix(k, opts.Prefix+opts.Delimiter)
131+
}
128132
keys := strings.Split(k, opts.Delimiter)
129133

130134
for i := len(keys) - 1; i >= 0; i-- {

flat_test.go

+120
Original file line numberDiff line numberDiff line change
@@ -388,3 +388,123 @@ func TestUnflatten(t *testing.T) {
388388
}
389389
}
390390
}
391+
392+
func TestFlattenPrefix(t *testing.T) {
393+
tests := []struct {
394+
given string
395+
options *Options
396+
want map[string]interface{}
397+
}{
398+
// test with different primitives
399+
// String: 'world',
400+
// Number: 1234.99,
401+
// Boolean: true,
402+
// null: null,
403+
{
404+
`{"hello": "world"}`,
405+
&Options{Prefix: "test", Delimiter: "."},
406+
map[string]interface{}{"test.hello": "world"},
407+
},
408+
{
409+
`{"hello": 1234.99}`,
410+
&Options{Prefix: "test", Delimiter: "_"},
411+
map[string]interface{}{"test_hello": 1234.99},
412+
},
413+
{
414+
`{"hello": true}`,
415+
&Options{Prefix: "test", Delimiter: "-"},
416+
map[string]interface{}{"test-hello": true},
417+
},
418+
{
419+
`{"hello":{"world":"good morning"}}`,
420+
&Options{Prefix: "test", Delimiter: "."},
421+
map[string]interface{}{"test.hello.world": "good morning"},
422+
},
423+
{
424+
`{"hello":{"world":1234.99}}`,
425+
&Options{Prefix: "test", Delimiter: "_"},
426+
map[string]interface{}{"test_hello_world": 1234.99},
427+
},
428+
{
429+
`{"hello":{"world":true}}`,
430+
&Options{Prefix: "test", Delimiter: "-"},
431+
map[string]interface{}{"test-hello-world": true},
432+
},
433+
}
434+
for i, test := range tests {
435+
var given interface{}
436+
err := json.Unmarshal([]byte(test.given), &given)
437+
if err != nil {
438+
t.Errorf("%d: failed to unmarshal test: %v", i+1, err)
439+
}
440+
got, err := Flatten(given.(map[string]interface{}), test.options)
441+
if err != nil {
442+
t.Errorf("%d: failed to flatten: %v", i+1, err)
443+
}
444+
if !reflect.DeepEqual(got, test.want) {
445+
t.Errorf("%d: mismatch, got: %v want: %v", i+1, got, test.want)
446+
}
447+
}
448+
}
449+
450+
func TestUnflattenPrefix(t *testing.T) {
451+
tests := []struct {
452+
flat map[string]interface{}
453+
options *Options
454+
want map[string]interface{}
455+
}{
456+
{
457+
map[string]interface{}{"test.hello": "world"},
458+
&Options{Prefix: "test", Delimiter: "."},
459+
map[string]interface{}{"hello": "world"},
460+
},
461+
{
462+
map[string]interface{}{"test_hello": 1234.56},
463+
&Options{Prefix: "test", Delimiter: "_"},
464+
map[string]interface{}{"hello": 1234.56},
465+
},
466+
{
467+
map[string]interface{}{"test-hello": true},
468+
&Options{Prefix: "test", Delimiter: "-"},
469+
map[string]interface{}{"hello": true},
470+
},
471+
// nested twice
472+
{
473+
map[string]interface{}{"test.hello.world.again": "good morning"},
474+
&Options{Prefix: "test", Delimiter: "."},
475+
map[string]interface{}{
476+
"hello": map[string]interface{}{
477+
"world": map[string]interface{}{
478+
"again": "good morning",
479+
},
480+
},
481+
},
482+
},
483+
// custom delimiter
484+
{
485+
map[string]interface{}{
486+
"test hello world again": "good morning",
487+
},
488+
&Options{
489+
Prefix: "test",
490+
Delimiter: " ",
491+
},
492+
map[string]interface{}{
493+
"hello": map[string]interface{}{
494+
"world": map[string]interface{}{
495+
"again": "good morning",
496+
},
497+
},
498+
},
499+
},
500+
}
501+
for i, test := range tests {
502+
got, err := Unflatten(test.flat, test.options)
503+
if err != nil {
504+
t.Errorf("%d: failed to unflatten: %v", i+1, err)
505+
}
506+
if !reflect.DeepEqual(got, test.want) {
507+
t.Errorf("%d: mismatch, got: %v want: %v", i+1, got, test.want)
508+
}
509+
}
510+
}

0 commit comments

Comments
 (0)