@@ -6,6 +6,7 @@ package maps
66import (
77 "fmt"
88 "reflect"
9+ "sort"
910 "strings"
1011
1112 "github.com/mitchellh/copystructure"
@@ -65,14 +66,22 @@ func flatten(m map[string]any, keys []string, delim string, out map[string]any,
6566// and returns a nested map where the keys are split into hierarchies by the given
6667// delimiter. For instance, `parent.child.key: 1` to `{parent: {child: {key: 1}}}`
6768//
69+ // Keys are sorted by length to make merge-order deterministic so that nested keys
70+ // don't end up overwriting their parent keys. eg: `parent.child` -> `parent`.
71+ //
6872// It's important to note that all nested maps should be
6973// map[string]any and not map[any]any.
7074// Use IntfaceKeysToStrings() to convert if necessary.
7175func Unflatten (m map [string ]any , delim string ) map [string ]any {
7276 out := make (map [string ]any )
7377
74- // Iterate through the flat conf map.
75- for k , v := range m {
78+ ks := make ([]string , 0 , len (m ))
79+ for k := range m {
80+ ks = append (ks , k )
81+ }
82+ sort .Strings (ks )
83+
84+ for _ , k := range ks {
7685 var (
7786 keys []string
7887 next = out
@@ -87,19 +96,17 @@ func Unflatten(m map[string]any, delim string) map[string]any {
8796 // Iterate through key parts, for eg:, parent.child.key
8897 // will be ["parent", "child", "key"]
8998 for _ , k := range keys [:len (keys )- 1 ] {
90- sub , ok := next [k ]
99+ // Create the key if it doesn't exist.
100+ sub , ok := next [k ].(map [string ]any )
91101 if ! ok {
92- // If the key does not exist in the map, create it.
93102 sub = make (map [string ]any )
94103 next [k ] = sub
95104 }
96- if n , ok := sub .(map [string ]any ); ok {
97- next = n
98- }
105+ next = sub
99106 }
100107
101108 // Assign the value.
102- next [keys [len (keys )- 1 ]] = v
109+ next [keys [len (keys )- 1 ]] = m [ k ]
103110 }
104111 return out
105112}
0 commit comments