1. Overview
Camel case and snake case are two well-known naming conventions.
In this tutorial, we’ll explore how to convert between camel and snake case strings in Kotlin.
2. Camel Case and Snake Case
Camel case is a convention where words are concatenated together, and the first letter of each subsequent word is capitalized, such as “FlywayMigrator“. Camel case is the naming convention applied by Java and Kotlin.
On the other hand, snake case is a convention that underscores separate words, such as “flyway_migrator“. Some programming languages, such as Python, conventionally use it for variable names, function names, etc.
So next, let’s look at how to convert between camel and snake cases in Kotlin.
3. Converting Camel Case to Snake Case
Let’s first convert camel case strings to snake case ones.
A camel case can begin with an uppercase or lowercase letter. Next, let’s create some example camel case strings to cover these cases as inputs and the expected conversation results:
val camelCaseInput1 = "myNiceCamelCaseString"
val camelCaseInput2 = "MyNiceCamelCaseString"
val expectedInSnakeCase = "my_nice_camel_case_string"
We’ll see two approaches to converting camel case strings to snake case. For simplicity, we’ll use unit test assertions to verify if each method works as expected.
3.1. Using the replace() Function With Regex
The first idea to solve the problem is using regex replacement. If we can replace each uppercase letter X with a “*_*” and its lowercase letter x, the problem can be solved. However, there is one exceptional case: when the first letter in the input is uppercase, we won’t prepend the underscore character.
So next, let’s see how we can achieve this using regex replacement:
fun String.camelToSnakeCase(): String {
val pattern = "(?<=.)[A-Z]".toRegex()
return this.replace(pattern, "_$0").lowercase()
}
As the code above shows, we’ve created an extension function to make the function call more natural. Further, we use the “*(?<=.)[A-Z]*” regex pattern to match the uppercase letter X that we want to replace with “_x” later. The regex is a positive look-behind expression that matches any uppercase letter preceded by any character. In other words, if the input begins with an uppercase letter, this letter won’t be matched, as there is no character before it.
Then we call this.replace(pattern, “_$0”) to apply the replacement. The “*$0*” in the replacement indicates the matched string. Finally, we convert the string after the replacement to lowercase to get the snake case string.
We can write the following tests to verify if the function works as expected:
assertEquals(expectedInSnakeCase, camelCaseInput1.camelToSnakeCase())
assertEquals(expectedInSnakeCase, camelCaseInput2.camelToSnakeCase())
3.2. Using the fold() Function
Alternatively, we can solve the problem using the fold() function:
fun String.camelToSnakeCaseNoRegex(): String {
return this.fold(StringBuilder()) { acc, c ->
acc.let {
val lowerC = c.lowercase()
acc.append(if (acc.isNotEmpty() && c.isUpperCase()) "_$lowerC" else lowerC)
}
}.toString()
}
assertEquals(expectedInSnakeCase, camelCaseInput1.camelToSnakeCaseNoRegex())
assertEquals(expectedInSnakeCase, camelCaseInput2.camelToSnakeCaseNoRegex())
As the function above shows, this approach iterates over each character in the camel case string and determines when to prepend “*_*“. Finally, we concatenate each processed character back to the result. It’s worth mentioning that StringBuilder is used for better performance.
4. Converting Snake Case to Camel Case
Now, let’s look at the conversion in the opposite direction. Again, let’s prepare the input snake case string and the expected result:
val snakeCaseInput = "one_good_snake_case_string"
val expectedInCamelCase = "oneGoodSnakeCaseString"
We’ll still use a regex-based replace() function to do the job:
fun String.snakeToCamelCase(): String {
val pattern = "_[a-z]".toRegex()
return replace(pattern) { it.value.last().uppercase() }
}
The function above matches each “_x” in the input and replaces it with “_X“. As the matched string always has two characters, we’ve used last().uppercase() to convert the last letter to uppercase. Alternatively, we can do the same using regex grouping:
fun String.snakeToCamelCase2(): String {
val pattern = "_([a-z])".toRegex()
return replace(pattern) { it.groupValues[1].uppercase() }
}
Finally, let’s test the two extension functions:
assertEquals(expectedInCamelCase, snakeCaseInput.snakeToCamelCase())
assertEquals(expectedInCamelCase, snakeCaseInput.snakeToCamelCase2())
5. Conclusion
In this article, we’ve learned how to convert between camel case and snake case strings through examples. As we’ve seen, regex is a handy technique for string conversion tasks.
As usual, the implementation of all these examples is over on GitHub.