Due to an excercise, I've made a function that converts given string to spinal case just-like-this-one (all lower case). I did it with two regular expressions. Can it be done with one, or generally better?

function spinalCase(str) {
  //if string is not delimited 
  if(str.search(/[_\s]/) === -1){
    // insert dash before every uppercase letter
    return str.replace(/(?=[A-Z])/g , '-').toLowerCase();
  } else {    
    //change delimiters to dashes  
    return str.replace(/[_\s](?=[a-zA-Z])/g, '-').toLowerCase();

edit: variable naming was forced by excercise (wouldn't complete otherwise). Example converted input:

"This Is Spinal Tap"



"Teletubbies say Eh-oh"


2 Answers 2


This does it in two steps:

  1. Add spaces before each uppercase, which allows the uppercases to be handled by...
  2. The delimiter case. This is the same regex you already wrote, although I am collapsing multiple delimters down to a single one.

Having two transformation passes, similar to using a unix pipeline, is easier to reason about that breaking it up into two cases with an "if else".

Note the cryptic (?!^) matches "not at the beginning of a line" only

function spinalCase(str) {
  return str.replace(/(?!^)([A-Z])/g, ' $1')
            .replace(/[_\s]+(?=[a-zA-Z])/g, '-').toLowerCase();

All of your test cases pass:


Well, to start, you have 3 regexes and duplicate code. str, while it is implied as a string, should be named to something that it is, like stringToConvert.

Now you're trying to jam in a lot of different inputs into one function. I highly suggest you split them into different functions instead. This keeps them predictable and easy to maintain. This will avoid cases like "Oh! I want to preserve _" and stuff but your function clobbers it, regardless.

In that case, CamelCase piece can be simplified to the following:

function camelCaseToSpinalCase(stringToReplace) {
  return stringToReplace.replace(/[A-Z]/g, function(match, offset) {
    return (offset ? '-' : '') + match;


replace accepts a string or pattern to replace and a function that generates the result. In this case, we don't have parenthesized blocks so we only get match and offset and no additional args in between. In all cases, we lowercase the text. If offset is 0, we're at the beginning of the text, meaning we don't add the dash before the text.

The snake-case piece into:

function snakeCaseToSpinalCase(stringToReplace) {
  return stringToReplace.replace(/_/g, '-').toLowerCase();


  • 2
    \$\begingroup\$ Except it won't work for some variations. I should've added requirements before, sorry. Ive added example inputs that function should process correctly. \$\endgroup\$
    – Ketus
    Nov 5, 2015 at 16:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.