Golang iterate a nested map and change keys to Title case

I have a nested map with lower case characters. What I want to do is to iterate on the map and convert all the keys to Title case. I tried the following code below, but it is giving me weird results. Can someone guide me where I am going wrong?

package main

import (
    "fmt"
    "strings"
)

func main() {
    a := make(map[string]interface{})

    a["start"] = map[string]interface{}{
        "hello": 2,
        "world": 3,
        "here": map[string]interface{}{
            "baam": 123,
            "boom": "dsd",
        },
    }

    printMap(a)

    fmt.Println(a)
}

func printMap(a map[string]interface{}) {

    for k, v := range a {

        switch v.(type) {
        case map[string]interface{}:
            printMap(v.(map[string]interface{}))
        default:
            title := strings.Title(k)
            a[title] = a[k]
            delete(a, k)
        }
    }
}

Upvotes: 0

Views: 4776

Answers (1)

Burak Serdar
Burak Serdar

Reputation: 51497

The problem is you are iterating a map and changing it at the same time, but expecting the iteration would not see what you did. The relevant part of the code is:

for k, v := range a {
    title := strings.Title(k)
    a[title] = a[k]
    delete(a, k)
}

So if the map has {"hello":2, "world":3}, and assume the keys are iterated in that order. After the first iteration, you now have:

{"world":3, "Hello":2}

Next iteration:

{"World":3, "Hello":2}

Next iteration looks at "Hello", which is already capitalized, so you capitalize it again, and then delete it, ending up with:

{"World":3}

You might want to produce a new map instead of overwriting the existing one, and return that so the caller can use it instead.

func main() {
    a := make(map[string]interface{})

    a["start"] = map[string]interface{}{
        "hello": 2,
        "world": 3,
        "here": map[string]interface{}{
            "baam": 123,
            "boom": "dsd",
        },
    }

    a=printMap(a)

    fmt.Println(a)
}

func printMap(a map[string]interface{}) map[string]interface{} {
    newMap:=map[string]interface{}{}
    for k, v := range a {

        switch v.(type) {
        case map[string]interface{}:
            newMap[k]=printMap(v.(map[string]interface{}))
        default:
            title := strings.Title(k)
            newMap[title] = a[k]
        }
    }
    return newMap
}

Upvotes: 6

Related Questions