Stack
Stack

Reputation: 529

camelCase to kebab-case

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')

Upvotes: 36

Views: 35280

Answers (9)

3limin4t0r
3limin4t0r

Reputation: 21130

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;
  // const upper = /(?<=\P{Lu})\p{Lu}|\p{Lu}(?=\P{Lu})/gu; // for short
  return string.replace(upper, "-$&").replace(/^-/, "").toLowerCase();
}


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

console.log(result);

This snippet replaces each uppercase character (\p{Uppercase_Letter}) before or after a non-uppercase character (\P{Uppercase_Letter}) with - followed by the matched uppercase character.

Example: BooBARBaz » -Boo-BAR-Baz each B has either a non-uppercase character before or after it.

After adding the - characters it removes the - at the start of the string (if there is any) and downcases the whole string.

Upvotes: 4

Jorge Del Castillo
Jorge Del Castillo

Reputation: 338

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

Upvotes: 0

Syed Ali Naqi Hasni
Syed Ali Naqi Hasni

Reputation: 1

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

Upvotes: 0

Moses Darko
Moses Darko

Reputation: 11

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(''))

Upvotes: 0

NNL993
NNL993

Reputation: 472

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))

Upvotes: 0

ABabin
ABabin

Reputation: 2947

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

Upvotes: 62

Arad
Arad

Reputation: 12804

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();
}

Upvotes: 17

Marc Danting
Marc Danting

Reputation: 111

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.

Upvotes: 11

Nicolae Maties
Nicolae Maties

Reputation: 2675

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.

Upvotes: 33

Related Questions