Skip to content

Commit 34a81f8

Browse files
committed
Implemenation for camel to snake case conversion
1 parent 7806006 commit 34a81f8

File tree

2 files changed

+124
-0
lines changed

2 files changed

+124
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package com.baeldung.scala.strings.cameltosnake
2+
3+
import scala.annotation.tailrec
4+
5+
object CamelToSnakeConversion {
6+
7+
def usingRegex(camelCaseString: String): String = {
8+
val regex = "([A-Z])".r
9+
regex.replaceAllIn(
10+
camelCaseString,
11+
m => s"_${m.group(1).toLowerCase}"
12+
)
13+
}
14+
15+
def usingFoldLeft(camelCaseString: String): String = {
16+
camelCaseString.foldLeft("") { (acc, char) =>
17+
if (char.isUpper) {
18+
if (acc.isEmpty) char.toLower.toString
19+
else acc + "_" + char.toLower
20+
} else {
21+
acc + char
22+
}
23+
}
24+
}
25+
26+
def handleAcronymsWithRegex(camelCaseString: String): String = {
27+
camelCaseString
28+
.replaceAll("([A-Z]+)([A-Z][a-z])", "$1_$2")
29+
.replaceAll("([a-z\\d])([A-Z])", "$1_$2")
30+
.toLowerCase
31+
}
32+
33+
def usingFlatMap(camelCase: String): String = {
34+
camelCase
35+
.flatMap { c =>
36+
if (c.isUpper) List('_', c) else List(c)
37+
}
38+
.mkString
39+
.toLowerCase
40+
}
41+
42+
def usingPatternMatching(camelCase: String): String = {
43+
@tailrec
44+
def rec(chars: List[Char], acc: List[Char]): List[Char] = {
45+
chars match {
46+
case Nil => acc
47+
case a :: tail if a.isUpper => rec(tail, acc ++ Seq('_', a))
48+
case a :: tail => rec(tail, acc ++ Seq(a))
49+
}
50+
}
51+
rec(camelCase.toList, Nil).mkString.toLowerCase
52+
}
53+
54+
def usingCollect(camelCase: String): String = {
55+
camelCase
56+
.collect {
57+
case c if c.isUpper => Seq('_', c)
58+
case c => Seq(c)
59+
}
60+
.flatten
61+
.mkString
62+
.toLowerCase
63+
}
64+
65+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package com.baeldung.scala.strings.cameltosnake
2+
3+
import org.scalatest.flatspec.AnyFlatSpec
4+
import org.scalatest.matchers.should.Matchers
5+
import org.scalatest.prop.TableDrivenPropertyChecks
6+
7+
class CamelToSnakeConversionUnitTest
8+
extends AnyFlatSpec
9+
with Matchers
10+
with TableDrivenPropertyChecks {
11+
12+
private val table = Table(
13+
("input", "expected"),
14+
("thisIsCamelCase", "this_is_camel_case"),
15+
("isThisCamel?", "is_this_camel?"),
16+
("alllower", "alllower"),
17+
("thisIsUSA", "this_is_u_s_a"),
18+
("xmlHttpRequest", "xml_http_request"),
19+
("convertXMLToJSON", "convert_x_m_l_to_j_s_o_n"),
20+
("parseHTML", "parse_h_t_m_l"),
21+
("classOfT", "class_of_t")
22+
)
23+
private val fns = Seq(
24+
("usingRegex", CamelToSnakeConversion.usingRegex),
25+
("usingFoldLeft", CamelToSnakeConversion.usingFoldLeft),
26+
("usingFlatMap", CamelToSnakeConversion.usingFlatMap),
27+
("usingPatterMatching", CamelToSnakeConversion.usingPatternMatching),
28+
("usingCollect", CamelToSnakeConversion.usingCollect)
29+
)
30+
31+
it should "convert camel to snake case" in {
32+
forAll(table) { (camel, expectedSnake) =>
33+
fns.map { (name, fn) =>
34+
withClue("function name: " + name) {
35+
fn(camel) shouldBe expectedSnake
36+
}
37+
}
38+
}
39+
}
40+
41+
it should "handle camel case starting with upper case character" in {
42+
val output = CamelToSnakeConversion.usingRegex("HelloWorld")
43+
output.stripPrefix("_") shouldBe "hello_world"
44+
}
45+
46+
it should "handle special case" in {
47+
Seq(
48+
("HelloWorld", "hello_world"),
49+
("thisIsUSA", "this_is_usa"),
50+
("convertXMLToJSON", "convert_xml_to_json"),
51+
("xmlToHTTPRequest", "xml_to_http_request")
52+
).map { (in, out) =>
53+
val output = CamelToSnakeConversion.handleAcronymsWithRegex(in)
54+
output shouldBe out
55+
}
56+
57+
}
58+
59+
}

0 commit comments

Comments
 (0)