Skip to content

Instantly share code, notes, and snippets.

@rheinardkorf
Created May 28, 2018 11:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rheinardkorf/b32a200cdcc6ed3ad2156210acad8e82 to your computer and use it in GitHub Desktop.
Save rheinardkorf/b32a200cdcc6ed3ad2156210acad8e82 to your computer and use it in GitHub Desktop.
Convert `map[string]interface{}` into a new `map[string]interface{}` with all keys converted to snake_case.
package snakeymap
func SnakeKeyMap(data map[string]interface{}) map[string]interface{} {
converted := make(map[string]interface{})
for key, val := range data {
goodKey := snakeKey(key)
switch v := val.(type) {
case map[string]interface{}:
converted[goodKey] = SnakeKeyMap(v)
default:
converted[goodKey] = v
}
}
return converted
}
func snakeKey(key string) string {
// Keep track of what kind of the kind of rune we're looking at.
const (
_ = iota // skip 0
lower
upper
digit
other
)
words := []string{}
var parts [][]rune
lastClass := 0
class := 0
// Split string
for _, r := range key {
switch true {
case unicode.IsLower(r):
class = lower
case unicode.IsUpper(r):
class = upper
case unicode.IsDigit(r):
class = digit
default:
class = other
}
if class == lastClass {
parts[len(parts)-1] = append(parts[len(parts)-1], r)
} else {
parts = append(parts, []rune{r})
}
lastClass = class
}
// Make sure we dont turn URL into U R L.
for i := 0; i < len(parts)-1; i++ {
if unicode.IsUpper(parts[i][0]) && unicode.IsLower(parts[i+1][0]) {
parts[i+1] = append([]rune{parts[i][len(parts[i])-1]}, parts[i+1]...)
parts[i] = parts[i][:len(parts[i])-1]
}
}
// Join all the strings together in the `words` slice.
for _, s := range parts {
if len(s) > 0 {
// Join the runes and make it lowercase.
words = append(words, strings.ToLower(string(s)))
}
}
// Finally return the snake_case.
return strings.Join(words, "_")
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment