25

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"

TER
  • 1,313
  • 2
  • 12
  • 14

7 Answers7

43

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
TER
  • 1,313
  • 2
  • 12
  • 14
  • 3
    Could be slightly shortened by: `regex.replace(this)` -> `replace(regex)` – Max Farsikov Oct 30 '20 at 07:45
  • 1
    `toLowerCase()` is deprecated in Kotlin since it does not use locale; one should use `lowercase()` – artaxerx May 13 '22 at 14:37
  • 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. @artaxerx is right that case should get updated as well. – Michael Nov 01 '22 at 21:24
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.

ysakhno
  • 834
  • 1
  • 7
  • 17
9

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.

Alexander Vasiljev
  • 1,974
  • 3
  • 19
  • 27
Per Huss
  • 4,755
  • 12
  • 29
4

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())
}
Sadeq
  • 61
  • 4
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

Hesham Fas
  • 876
  • 1
  • 9
  • 20
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)
Dane Lowe
  • 288
  • 3
  • 10
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()
}