Skip to content

Instantly share code, notes, and snippets.

@sidharthkuruvila
Created July 21, 2012 06:30
Show Gist options
  • Save sidharthkuruvila/3154845 to your computer and use it in GitHub Desktop.
Save sidharthkuruvila/3154845 to your computer and use it in GitHub Desktop.
Utility to functions to convert between camel case and underscore separated names
/**
* Takes a camel cased identifier name and returns an underscore separated
* name
*
* Example:
* camelToUnderscores("thisIsA1Test") == "this_is_a_1_test"
*/
def camelToUnderscores(name: String) = "[A-Z\\d]".r.replaceAllIn(name, {m =>
"_" + m.group(0).toLowerCase()
})
/*
* Takes an underscore separated identifier name and returns a camel cased one
*
* Example:
* underscoreToCamel("this_is_a_1_test") == "thisIsA1Test"
*/
def underscoreToCamel(name: String) = "_([a-z\\d])".r.replaceAllIn(name, {m =>
m.group(1).toUpperCase()
})
@amackillop
Copy link

amackillop commented Apr 10, 2019

@ruloweb This is nice but won't work if the two consecutive uppercase chars are at the start (ie. THisIsCamel) or if there are greater than two consecutive uppercase characters elsewhere (ie. ThisIsCAMel). Consequently, this will fail for converting all caps as well.

I adjusted the code to support the all caps case:

 def camel2Snake(str: String): String = {
    @tailrec
    def camel2SnakeRec(s: String, output: String, lastUppercase: Boolean): String =
      if (s.isEmpty) output
      else {
        val c = if (s.head.isUpper && !lastUppercase) "_" + s.head.toLower else s.head.toLower
        camel2SnakeRec(s.tail, output + c, s.head.isUpper && !lastUppercase)
      }
    if (str.forall(_.isUpper)) str.map(_.toLower)
    else {
      camel2SnakeRec(str, "", true)
    }
  }

@0dilon
Copy link

0dilon commented Dec 19, 2019

@amackillop, it doesnt't work for "THISIsADog" (it gives "t_hi_sis_adog")

i suggest the following code :

def camel2Snake(str: String): String = {

  val headInUpperCase = str.takeWhile(c => c.isUpper || c.isDigit)
  val tailAfterHeadInUppercase = str.dropWhile(c => c.isUpper || c.isDigit)

  if (tailAfterHeadInUppercase.isEmpty) headInUpperCase.toLowerCase else {
    val firstWord = if (!headInUpperCase.dropRight(1).isEmpty) {
      headInUpperCase.last match {
        case c: Any if (c.isDigit) => headInUpperCase
        case _ => headInUpperCase.dropRight(1).toLowerCase
      }
    } else {
      headInUpperCase.toLowerCase + tailAfterHeadInUppercase.takeWhile(c => c.isLower)
    }

    if (firstWord == str.toLowerCase) {
      firstWord
    } else {
      s"${firstWord}_${camel2Snake(str.drop(firstWord.length))}"
    }

  }
}

@nicusX
Copy link

nicusX commented Jan 2, 2020

@0dilon you version stack-overflows if the camelCase already contains an underscore.
e.g. camel2Snake("foo_BarBaz")

@jnewman
Copy link

jnewman commented May 18, 2021

Late to the party, but iterate w/ Chars seems fine too if you're ok w/ no handling -s in the Strings

val camelToKebab: String => String = _.foldLeft("") {
      case (acc, chr) if chr.isUpper => acc :+ '-' :+ chr.toLower
      case (acc, chr)                => acc :+ chr
    }

@Kalyan-D
Copy link

@0dilon you version stack-overflows if the camelCase already contains an underscore.

any update on this @0dilon

@umbarger
Copy link

umbarger commented Sep 21, 2021

val camelToSnake : String => String = _.foldLeft( "" ){ (acc, c) =>
    ( c.isUpper, acc.isEmpty, acc.takeRight(1) == "_" ) match {
      case (true, false, false) => acc + "_" + c.toLower
      case (true, _, _) => acc + c.toLower
      case (false, _, _) => acc + c
    }
  }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment