camelCase to kebab-case
Asked Answered
B

9

30

I have a kebabize function which converts camelCase to kebab-case. I am sharing my code. Can it be more optimized? I know this problem can be solved using regex. But, I want to do it without using regex.

const kebabize = str => {

    let subs = []
    let char = ''
    let j = 0

    for( let i = 0; i < str.length; i++ ) {

        char = str[i]

        if(str[i] === char.toUpperCase()) {
            subs.push(str.slice(j, i))
            j = i
        }

        if(i == str.length - 1) {
            subs.push(str.slice(j, str.length))
        }
    }

    return subs.map(el => (el.charAt(0).toLowerCase() + el.substr(1, el.length))).join('-')
}

kebabize('myNameIsStack')
Bludgeon answered 27/7, 2020 at 13:10 Comment(2)
Instead of optimizing your code, I really would advice you to use a library for this kind of util functions. For instance, use the _.kebabCase function from Lodash!Tomika
@YevgenGorbunkov Just improving my logical skills. That's why not using regex. I know this approach is cleaner and optimized.Bludgeon
R
33

const kebabize = str => {
   return str.split('').map((letter, idx) => {
     return letter.toUpperCase() === letter
      ? `${idx !== 0 ? '-' : ''}${letter.toLowerCase()}`
      : letter;
   }).join('');
}

console.log(kebabize('myNameIsStack'));
console.log(kebabize('MyNameIsStack'));

You can just check every letter is if upperCase or not and replace it.

Radicand answered 27/7, 2020 at 13:15 Comment(3)
This will work only if input string is camelCase, not UpperCamelCaseAdorno
@VinayMahamuni UpperCamelCase is PascalCase FYI, it's not the same.Latonya
str.split("") gives unexpected results with unicode symbols made up of multiple parts. Use [...str] or Array.from(str).Tamaru
P
54

I have a one-liner similar to Marc's but with a simpler Regular Expression and ~20% faster according my benchmark (Chrome 89).

const kebabize = (str) => str.replace(/[A-Z]+(?![a-z])|[A-Z]/g, ($, ofs) => (ofs ? "-" : "") + $.toLowerCase())

const words = ['StackOverflow', 'camelCase', 'alllowercase', 'ALLCAPITALLETTERS', 'CustomXMLParser', 'APIFinder', 'JSONResponseData', 'Person20Address', 'UserAPI20Endpoint'];

console.log(words.map(kebabize));

[A-Z]+(?![a-z]) matches any consecutive capital letters, excluding any capitals followed by a lowercase (signifying the next word). Adding |[A-Z] then includes any single capital letters. It must be after the consecutive capital expression, otherwise the expression will match all capital letters individually and never match consecutives.

String.prototype.replace can take a replacer function. Here, it returns the lowercased matched capital(s) for each word, after prefixing a hyphen when the match offset is truthy (not zero - not the first character of the string).

I suspect Marc's solution is less performant than mine because by using replace to insert hyphens and lowercasing the whole string afterwards, it must iterate over the string more than once, and its expression also has more complex look aheads/behind constructs.

Benchmark

Precondition answered 24/4, 2021 at 14:14 Comment(1)
Not sure if anyone else expects this but, -F will turn into --f.Abednego
R
33

const kebabize = str => {
   return str.split('').map((letter, idx) => {
     return letter.toUpperCase() === letter
      ? `${idx !== 0 ? '-' : ''}${letter.toLowerCase()}`
      : letter;
   }).join('');
}

console.log(kebabize('myNameIsStack'));
console.log(kebabize('MyNameIsStack'));

You can just check every letter is if upperCase or not and replace it.

Radicand answered 27/7, 2020 at 13:15 Comment(3)
This will work only if input string is camelCase, not UpperCamelCaseAdorno
@VinayMahamuni UpperCamelCase is PascalCase FYI, it's not the same.Latonya
str.split("") gives unexpected results with unicode symbols made up of multiple parts. Use [...str] or Array.from(str).Tamaru
B
17

RegEx is faster!

Unlike what you might think, the RegEx way of doing this is actually significantly faster! See benchmark.

The function below supports converting both camelCase and PascalCase into kebab-case:

function toKebabCase(str) {
    return str.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
}
Burnham answered 4/12, 2021 at 15:27 Comment(3)
This fails with fully capitalized words. JSONResponseData would return jsonresponse-data instead of json-response-data. I suppose it could be a stylistic choice, but it's not what I think most people would expect and it will cause issues if they're not aware.Precondition
@ABabin, but wouldn't be correct jsonRespondeData instead of JSONResponseData?Bullate
@Bullate There is no "correct" standard. Some people capitalize acronyms in camelCase - sometimes up to a certain length or selectively or all the time. The other solutions here handle those cases without significantly more overhead.Precondition
V
11

Here is my solution:

Works with camelCase and PascalCase:

let words = ['StackOverflow', 'camelCase', 'alllowercase', 'ALLCAPITALLETTERS', 'CustomXMLParser', 'APIFinder', 'JSONResponseData', 'Person20Address', 'UserAPI20Endpoint'];

let result = words.map(w => w.replace(/((?<=[a-z\d])[A-Z]|(?<=[A-Z\d])[A-Z](?=[a-z]))/g, '-$1').toLowerCase());

console.log(result);

/*

Returns:
[
  "stack-overflow",
  "camel-case",
  "alllowercase",
  "allcapitalletters",
  "custom-xml-parser",
  "api-finder",
  "json-response-data",
  "person20-address",
  "user-api20-endpoint"
]

*/

Explanation:

  1. Match any of the following regular expressions:
  • Find any capital letter, that is immediately preceeded by a small letter or a number, or
  • Find any capital letter, that is immediately preceeded by a capital letter or a number, that is immediately followed by a small letter
  1. Replace the captured position with a dash ('-') followed by the captured capital letter
  2. Finally, convert the whole string to lowercase.
Vey answered 31/3, 2021 at 0:27 Comment(1)
Doesn't work on safari.Convexoconcave
D
3

I would use something like this.

function kebabize(string) {
  // uppercase after a non-uppercase or uppercase before non-uppercase
  const upper = /(?<!\p{Uppercase_Letter})\p{Uppercase_Letter}|\p{Uppercase_Letter}(?!\p{Uppercase_Letter})/gu;
  return string.replace(upper, "-$&").replace(/^-/, "").toLowerCase();
}


const strings = ["myNameIsStack", "HTTPRequestData", "DataX", "Foo6HelloWorld9Bar", "Áb"];
const result  = strings.map(kebabize);

console.log(result);

This snippet replaces all uppercase characters before or after a non-uppercase character with - followed by the uppercase. It then removes the - at the start of the string (if there is any) and downcases the whole string.

Dumb answered 27/7, 2020 at 13:54 Comment(1)
be careful about this solution, Safari will fail because, Safari does not support the negative lookbehind yet.Recover
B
0

Simple solution for older browsers:

var str = 'someExampleString'
var i

function camelToKebab() {
  var __str = arguments[0]
  var __result = ''

  for (i = 0; i < __str.length; i++) {
    var x = __str[i]

    if(x === x.toUpperCase()) {
      __result += '-' + x.toLowerCase()
    } else {
      __result += x
    }
  }

  return __result
}

console.log(str, '->', camelToKebab(str))
Bullate answered 30/8, 2022 at 6:1 Comment(0)
V
0

Here is the solution I came up with:

let resultDiv = document.querySelector(".result");

let camelCase = "thisIsCamelCase";
let kebabCase;
kebabCase = camelCase.split('').map(el=> {
  const charCode = el.charCodeAt(0);
  if(charCode>=65 && charCode<=90){
    return "-" + el.toLowerCase() 
  }else{
    return el;
  }
})

return(kebabCase.join(''))
Vaishnava answered 30/8, 2022 at 6:29 Comment(1)
How about using the index provided by the map() and removing charCode? ``` kebabCase = camelCase.split('').map((el,index)=> { if(camelCase.charCodeAt(index)>=65 && camelCase.charCodeAt(index)<=90){ return "-" + el.toLowerCase() }else{ return el; } }) ```Millrun
B
0

Your current implementation does a good job, but there are a few ways to make it more efficient or concise. Here is a slightly optimized version of your function, keeping the core logic but streamlining it a bit:

const kebabize = str => {
    let result = ''; // Use a single string to build the result

    for (let i = 0; i < str.length; i++) {
        let char = str[i];
        
        // Check if the character is uppercase
        if (char === char.toUpperCase() && i !== 0) { // Add a dash before uppercase letters, except the first character
            result += '-';
        }
        
        result += char.toLowerCase(); // Add the lowercase version of the current character to the result
    }

    return result;
};

console.log(kebabize('myNameIsStack')); // Outputs: my-name-is-stack
Bergschrund answered 21/2 at 8:49 Comment(0)
P
0

In case someone needs to separate all caps for e.g

const example1 = 'someExampleString'
const example2 = 'multiWordTogetherXYZ'


function camelToKebabCase(strToChange) {
  return strToChange.replace(/(?:^|\.?)([A-Z])/g, (x, y) => '-' + y.toLowerCase()).replace(/^-/, '');
}

console.log(example1, '->', camelToKebabCase(example1)) // some-example-string
console.log(example2, '->', camelToKebabCase(example2)) // multi-word-together-x-y-z
Plugboard answered 14/5 at 18:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.