Create a gist now

Instantly share code, notes, and snippets.

Embed
What would you like to do?
ToSnake function for golang, convention-compliant
package main
import (
"unicode"
)
// ToSnake convert the given string to snake case following the Golang format:
// acronyms are converted to lower-case and preceded by an underscore.
func ToSnake(in string) string {
runes := []rune(in)
length := len(runes)
var out []rune
for i := 0; i < length; i++ {
if i > 0 && unicode.IsUpper(runes[i]) && ((i+1 < length && unicode.IsLower(runes[i+1])) || unicode.IsLower(runes[i-1])) {
out = append(out, '_')
}
out = append(out, unicode.ToLower(runes[i]))
}
return string(out)
}
package main
import (
"strings"
"testing"
)
type SnakeTest struct {
input string
output string
}
var tests = []SnakeTest{
{"a", "a"},
{"snake", "snake"},
{"A", "a"},
{"ID", "id"},
{"MOTD", "motd"},
{"Snake", "snake"},
{"SnakeTest", "snake_test"},
{"SnakeID", "snake_id"},
{"SnakeIDGoogle", "snake_id_google"},
{"LinuxMOTD", "linux_motd"},
{"OMGWTFBBQ", "omgwtfbbq"},
{"omg_wtf_bbq", "omg_wtf_bbq"},
}
func TestToSnake(t *testing.T) {
for _, test := range tests {
if ToSnake(test.input) != test.output {
t.Errorf(`ToSnake("%s"), wanted "%s", got \%s"`, test.input, test.output, ToSnake(test.input))
}
}
}
var benchmarks = []string{
"a",
"snake",
"A",
"Snake",
"SnakeTest",
"SnakeID",
"SnakeIDGoogle",
"LinuxMOTD",
"OMGWTFBBQ",
"omg_wtf_bbq",
}
func BenchmarkToSnake(b *testing.B) {
for i := 0; i < b.N; i++ {
for _, input := range benchmarks {
ToSnake(input)
}
}
}
func BenchmarkToLower(b *testing.B) {
for i := 0; i < b.N; i++ {
for _, input := range benchmarks {
strings.ToLower(input)
}
}
}
@dmitshur

This comment has been minimized.

Show comment
Hide comment

Neat. This is similar to what I've done at https://godoc.org/github.com/shurcooL/go/gists/gist6003701.

@azer

This comment has been minimized.

Show comment
Hide comment
@azer

azer Dec 19, 2015

could you make it a repo so we can import it @elwinar ?

also, you should test inputs like "APIResponse", too

update: I've created a repo to be able to import it; https://github.com/azer/snakecase

azer commented Dec 19, 2015

could you make it a repo so we can import it @elwinar ?

also, you should test inputs like "APIResponse", too

update: I've created a repo to be able to import it; https://github.com/azer/snakecase

@Conorbro

This comment has been minimized.

Show comment
Hide comment
@Conorbro

Conorbro Nov 3, 2017

Slightly modified version to include snake casing when strings contain numbers e.g. http2xx becomes http_2xx.

// ToSnakeCase convert the given string to snake case following the Golang format:
// acronyms are converted to lower-case and preceded by an underscore.
func ToSnakeCase(in string) string {
	runes := []rune(in)

	var out []rune
	for i := 0; i < len(runes); i++ {
		if i > 0 && (unicode.IsUpper(runes[i]) || unicode.IsNumber(runes[i])) && ((i+1 < len(runes) && unicode.IsLower(runes[i+1])) || unicode.IsLower(runes[i-1])) {
			out = append(out, '_')
		}
		out = append(out, unicode.ToLower(runes[i]))
	}

	return string(out)
}

Conorbro commented Nov 3, 2017

Slightly modified version to include snake casing when strings contain numbers e.g. http2xx becomes http_2xx.

// ToSnakeCase convert the given string to snake case following the Golang format:
// acronyms are converted to lower-case and preceded by an underscore.
func ToSnakeCase(in string) string {
	runes := []rune(in)

	var out []rune
	for i := 0; i < len(runes); i++ {
		if i > 0 && (unicode.IsUpper(runes[i]) || unicode.IsNumber(runes[i])) && ((i+1 < len(runes) && unicode.IsLower(runes[i+1])) || unicode.IsLower(runes[i-1])) {
			out = append(out, '_')
		}
		out = append(out, unicode.ToLower(runes[i]))
	}

	return string(out)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment