~/snippets/go-remove-dots-from-map-keys
Published on

Remove dots from map keys

1128 words6 min read
package main

import (
	"encoding/json"
	"fmt"
	"log"
	"strings"
)

type ListThing struct {
	ID      int32               `json:"id,omitempty"`
	Version string              `json:"version,omitempty"`
	Conf    map[string][]string `json:"conf,omitempty"`
}

type DoubleNestedThing struct {
	DoubleNestedA string              `json:"doubleNestedA,omitempty"`
	DoubleNestedB int                 `json:"doubleNestedB,omitempty"`
	DoubleNestedC bool                `json:"doubleNestedC,omitempty"`
	DoubleNestedD []string            `json:"doubleNestedD,omitempty"`
	DoubleNestedE map[string][]string `json:"doubleNestedE,omitempty"`
}

type NestedThing struct {
	NestedA string            `json:"nestedA,omitempty"`
	NestedB int               `json:"nestedB,omitempty"`
	NestedC bool              `json:"nestedC,omitempty"`
	NestedD []string          `json:"nestedD,omitempty"`
	NestedE DoubleNestedThing `json:"nestedE,omitempty"`
}

type MainThing struct {
	FieldA string              `json:"fieldA,omitempty"`
	FieldB int                 `json:"fieldB,omitempty"`
	FieldC bool                `json:"fieldC,omitempty"`
	FieldD []string            `json:"fieldD,omitempty"`
	FieldE NestedThing         `json:"fieldE,omitempty"`
	FieldF map[string][]string `json:"fieldF,omitempty"`
	FieldG []ListThing         `json:"fieldG,omitempty"`
}

func main() {
	mt := MainThing{
		FieldA: "This is a field woo",
		FieldB: 1358,
		FieldC: true,
		FieldD: []string{"this", "is", "a", "list", "of", "strings"},
		FieldE: NestedThing{
			NestedA: "oh look at me, I'm nested",
			NestedB: 200,
			NestedC: false,
			NestedD: []string{"check", "out", "this", "list"},
			NestedE: DoubleNestedThing{
				DoubleNestedA: "So nested I'm going to sleep",
				DoubleNestedB: 10,
				DoubleNestedC: true,
				DoubleNestedD: []string{"into", "the", "darkness"},
				DoubleNestedE: map[string][]string{
					"thisisakey":                       {"this", "is", "its", "value"},
					"i can include spaces":             {"and", "it", "works"},
					"i.can.also.include.dots":          {"which", "we", "want", "to", "remove"},
					"wi_bbl_y-wo.bb.l_y-foo_bar.whizz": {"what", "a", "strange", "key"},
				},
			},
		},
		FieldF: map[string][]string{
			"weeeeeeeeeeeeeeee":                   {"eeee", "eeee", "eee", "eee"},
			"woooo_oooo_oooo_oooo":                {"ahhh", "aaaaa", "bbbbb"},
			"wizz.zzzz.zzzz.zzzz":                 {"should", "be", "amended"},
			"weee_ooooo_oooo_eeee.zzz.zzz.eee.zz": {"w", "t", "f", "?"},
		},
		FieldG: []ListThing{
			{ID: 1, Version: "0.0.1", Conf: map[string][]string{"nodots": {"whatever"}}},
			{ID: 2, Version: "0.0.2", Conf: map[string][]string{"some_underscores": {"whatever"}}},
			{ID: 3, Version: "0.0.3", Conf: map[string][]string{"now.some.dots": {"whatever"}}},
		},
	}

	b, err := json.Marshal(mt)
	if err != nil {
		log.Fatal(err)
	}

	var tp map[string]interface{}
	if err := json.Unmarshal(b, &tp); err != nil {
		log.Fatal(err)
	}

	tp = removeDotFromKeys(tp)

	fmt.Println(tp)
}

func removeDotFromKeys(m map[string]interface{}) map[string]interface{} {
	nm := make(map[string]interface{})

	for key, value := range m {
		if v, ok := value.(map[string]interface{}); ok {
			nm[key] = removeDotFromKeys(v)
			continue
		}

		if s, ok := value.([]interface{}); ok {
			for i, sv := range s {
				if v, ok := sv.(map[string]interface{}); ok {
					s[i] = removeDotFromKeys(v)
				}
			}
			nm[strings.ReplaceAll(key, ".", "_")] = s
			continue
		}

		nm[strings.ReplaceAll(key, ".", "_")] = value
	}

	return nm
}