How can I convert a camel case string to snake case and back in idiomatic Kotlin?
Asked Answered
H

7

26

Looking for code that will do conversions like this: "MyCamelCaseA" to "my_camel_case_a" "AMultiWordString" to "a_multi_word_string" "my_camel_case_a" to "myCamelCaseA" or "MyCamelCaseA" "a_multi_word_string" to "aMultiWordString" or "AMultiWordString"

Hospitable answered 31/1, 2020 at 19:43 Comment(1)
Can you accept an answer?Pennyroyal
H
46

Here are extensions to the String class that use regex and replacements to convert a string from camel case to snake case, and from snake case to camel case:

val camelRegex = "(?<=[a-zA-Z])[A-Z]".toRegex()
val snakeRegex = "_[a-zA-Z]".toRegex()

// String extensions
fun String.camelToSnakeCase(): String {
    return camelRegex.replace(this) {
        "_${it.value}"
    }.toLowerCase()
}

fun String.snakeToLowerCamelCase(): String {
    return snakeRegex.replace(this) {
        it.value.replace("_","")
            .toUpperCase()
    }
}

fun String.snakeToUpperCamelCase(): String {
    return this.snakeToLowerCamelCase().capitalize()
}

Here are examples using the String extension:

print("${"MyCamelCaseA".camelToSnakeCase()}\n")
my_camel_case_a
print("${"AMultiWordString".camelToSnakeCase()}\n")
a_multi_word_string
"my_camel_case_a".snakeToLowerCamelCase()
myCamelCaseA
"my_camel_case_a".snakeToUpperCamelCase()
MyCamelCaseA
Hospitable answered 31/1, 2020 at 19:43 Comment(3)
Could be slightly shortened by: regex.replace(this) -> replace(regex)Edmond
toLowerCase() is deprecated in Kotlin since it does not use locale; one should use lowercase()Unrestrained
I think you can enhance this answer with converting the snakeToLowerCamelCase to this to account for capital snakes. toLowerCase().replace(snakeRegex) { it.value.replace("_","") .toUpperCase() } I'm using an old version Kotlin though. @Unrestrained is right that case should get updated as well.Ergocalciferol
R
12

I would go with these implementations:

fun String.toCamelCase() = 
    split('_').joinToString("", transform = String::capitalize)

... which splits the string using snakes as delimiters, and then reattaches the parts as capitalized words without a delimiter.

fun String.toSnakeCase() = replace(humps, "_").lowercase()
private val humps = "(?<=.)(?=\\p{Upper})".toRegex()

... which uses a regex to find the positions before humps, inserting snakes, and then converts the whole string to lowercase. The regex consists of two parts, the first one (?<=.) is a positive look-behind saying that it must be preceded by a character, and the second part (?=\\p{Upper}) is using a positive look-ahead saying it must be followed by an uppercase character.

Rangoon answered 12/1, 2021 at 8:44 Comment(1)
I like this answer because 1) it's the cleanest, and 2) I said "dang, I should have thought of that."Pennyroyal
R
9

Here's my stab at this.

fun String.camelToSnakeCase() = fold(StringBuilder(length)) { acc, c ->
    if (c in 'A'..'Z') (if (acc.isNotEmpty()) acc.append('_') else acc).append(c + ('a' - 'A'))
    else acc.append(c)
}.toString()

My approach is also written in the form of extension function, but it does not use regular expressions, instead going character-by-character, processing them and folding the processing result into the accumulator, which at the beginning is an empty StringBuilder. The processing is as follows:

  • if the character is not an upper-case Latin letter, add it to accumulator as is
  • if the character is an upper-case Latin letter, then also check if this is not the first character of the string (accumulator is not empty). If it is not, then add underscore to accumulator. Finally add lower-cased character.

One thing to note, is that kotlin.text.StringBuilder is used, not the JDK one.

Rosinarosinante answered 31/1, 2020 at 21:27 Comment(0)
L
5

If you have jackson-databind in your classpath, you can use the following utility function:

import com.fasterxml.jackson.databind.PropertyNamingStrategies

fun String.toSnakeCase(): String = 
    PropertyNamingStrategies.SnakeCaseStrategy().translate(this)

fun main() {
    // should output this_is_the_case
    println("thisIsTheCase".toSnakeCase())
}
Lallation answered 6/8, 2022 at 1:20 Comment(0)
F
1

this is my try with kotlin only

   val  camelCaseString = "thisIsCamelCase"
    val snakeCaseString = camelCaseString.map {
        if (it.isUpperCase()){
            "_${it.toLowerCase()}"
        }else
{"$it"}
    }
.joinToString(separator = "")
System.out.println("here is your snake string: $snake_case_string")

here is your snake string: this_is_camel_case

convert from snake to camel

val snakeCaseString = "snake_case_string"
val camelCase = StringBuilder()
var prevChar = '$'
snakeCaseString.forEach {
if(prevChar.equals('_')){
    camelCase.append(it.toUpperCase())
}else if(!it.equals('_')){
    camelCase.append(it)
}
    prevChar = it
}

System.out.println(camelCase.toString())

snakeCaseString

Finnell answered 10/5, 2020 at 8:40 Comment(0)
S
1

I took one of the answers here, added Title Case and changed the API a bit

val camelRegex = "(?<=[a-zA-Z])[A-Z]".toRegex()
val snakeRegex = "_[a-zA-Z]".toRegex()

@JvmInline
value class SnakeCaseString(private val string: String) {
    fun toCamelCase(): String = snakeRegex.replace(string) { it.value.replace("_", "").uppercase() }
    fun toUpperCamelCase(): String =
        toCamelCase().replaceFirstChar { if (it.isLowerCase()) it.titlecase() else it.toString() }
    fun toTitleCase(): String = snakeRegex.replace(string) { it.value.replace("_", " ").uppercase() }
        .replaceFirstChar { if (it.isLowerCase()) it.titlecase() else it.toString() }
}

@JvmInline
value class CamelCaseString(private val string: String) {
    fun toSnakeCase(): String = camelRegex.replace(string) { "_${it.value}" }.lowercase()
    fun toTitleCase(): String = camelRegex.replace(string) { "_${it.value}" }
        .replaceFirstChar { if (it.isLowerCase()) it.titlecase() else it.toString() }
}

fun String.asSnakeCase() = SnakeCaseString(this)
fun String.asCamelCase() = CamelCaseString(this)
Shanan answered 19/7, 2021 at 22:29 Comment(0)
T
0

If you want a method with an input and output, this is how I did it:

private fun convertCamelToSnakeCase(camelCase : String) : String {
    val snakeCase = StringBuilder()
    for(character in camelCase) {
        if(character.isUpperCase()) {
            snakeCase.append("_${character.toLowerCase()}")
        } else {
            snakeCase.append(character)
        }
    }
    return snakeCase.removePrefix("_").toString()
}
Tales answered 7/12, 2020 at 14:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.