camelCase to dash - two capitals next to each other

10.2k views Asked by At

I'm using this function to convert CamelCase to dashed string:

function camel2dashed($className) {
    return strtolower(preg_replace('/([^A-Z-])([A-Z])/', '$1-$2', $className));
}

it kinda works but theres problem when I have for ex. this string: getADog. It returns get-adog but I want get-a-dog

how should I change my code? Thanks

4

There are 4 answers

0
Mark Byers On BEST ANSWER

Use a lookahead assertion:

function camel2dashed($className) {
    return strtolower(preg_replace('/([a-zA-Z])(?=[A-Z])/', '$1-', $className));
}

See it working online: ideone

0
Ergwun On

You don't need a lookahead assertion to do this if you know that your string doesn't start with an upper-case letter, you can just insert a hyphen before every upper-case letter like this:

function camel2dashed($className) {
    return strtolower(preg_replace('/([A-Z])/', '-$1', $className));
}

This still won't handle cases like @sfjedi's "companyHQ" -> "company-hq". For that you'd have to explicitly test for permitted capitalized substrings that shouldn't be split, or specify some generic rules (e.g. don't prepend hyphen before last character).

You can find some more sophisticated alternatives in the answers to this virtual duplicate question.

0
Riki137 On
function camel2dash($name) {
   return ltrim(strtolower(preg_replace('/[A-Z]([A-Z](?![a-z]))*/', '-$0', $name)), '-');
}

Handles hopefully all edge cases:

SomeOfTheWords → some-of-the-words
getADog → get-a-dog
ADog → a-dog
aDog → a-dog
XML → xml
SimpleXMLReader → simple-xml-reader
0
mickmackusa On

Your camelCase to kebab-case pattern doesn't need to capture anything or use a backreference in the replacement.

Code: (Demo)

$className = 'getADog';
echo strtolower(preg_replace('/[a-zA-Z]\K(?=[A-Z])/', '-', $className));
// get-a-dog

For a regex that is more robust and will handle camelCase and StudlyCase and will correctly handle acronyms and numeric strings, try the following. (Demo)

(?>               #start atomic group (use first qualifying branch)
   [A-Z]?[a-z]+   #zero or one upper followed by one or more lowers
   |              #OR
   [A-Z]+         #one or more uppers
   (?=            #start lookahead without consuming characters
      [A-Z][a-z]  #one upper followed by one lower
      |           #OR
      [^A-Za-z]   #one non-letter
   )              #end lookahead
   |              #OR
   [^A-Za-z]+     #one or more non-letters
)                 #end atomic group
\K                #forget any consumed matched characters so far
(?!$)             #position must not be the end of the string

Code: (Demo)

$tests = [
    'getADogThenROCKInTheUSA',
    'SomeOfTheWords',
    'getADog',
    'ADog',
    'aDog',
    'XML',
    'SimpleXMLReader',
    'FREE4NOW',
    '123ABC',
    'DEF456',
    'ghi789',
    'wtf666Omg',
    'PHP8Point3',
    
];
foreach ($tests as $test) {
    printf(
        "%25s => %s\n",
        $test,
        strtolower(
            preg_replace(
                '/(?>[A-Z]?[a-z]+|[A-Z]+(?=[A-Z][a-z]|[^A-Za-z])|[^A-Za-z]+)\K(?!$)/',
                '-',
                $test
            )
        )
    );
}

Output:

  getADogThenROCKInTheUSA => get-a-dog-then-rock-in-the-usa
           SomeOfTheWords => some-of-the-words
                  getADog => get-a-dog
                     ADog => a-dog
                     aDog => a-dog
                      XML => xml
          SimpleXMLReader => simple-xml-reader
                 FREE4NOW => free-4-now
                   123ABC => 123-abc
                   DEF456 => def-456
                   ghi789 => ghi-789
                wtf666Omg => wtf-666-omg
               PHP8Point3 => php-8-point-3