93

I have a regular expression in JavaScript to split my camel case string at the upper-case letters using the following code (which I subsequently got from here):

"MyCamelCaseString"
    .replace(/([A-Z])/g, ' $1')
    .replace(/^./, function(str){ return str.toUpperCase(); })

Thus that returns:

"My Camel Case String"

Which is good. However, I want to step this up a notch. Could someone help me with a regex which will split if, and only if, the former character is lower-case and the latter is upper-case.

Thus, the above example will be the result I expect, but if I do:

"ExampleID"

Then I get returned:

"Example ID"

Instead of

"Example I D"

Since it's splitting at each upper-case and ignoring anything before it.

Hope that makes sense! And thanks :).

Community
  • 1
  • 1
keldar
  • 6,152
  • 10
  • 52
  • 82

15 Answers15

177

My guess is replacing /([A-Z])/ with /([a-z])([A-Z])/ and ' $1' with '$1 $2'

"MyCamelCaseString"
    .replace(/([a-z])([A-Z])/g, '$1 $2');

/([a-z0-9])([A-Z])/ for numbers counting as lowercase characters

console.log("MyCamelCaseStringID".replace(/([a-z0-9])([A-Z])/g, '$1 $2'))
Limbo
  • 2,123
  • 21
  • 41
Michiel Dral
  • 3,932
  • 1
  • 19
  • 21
  • 2
    @keldar This won't work for numbers though. `Test1Test2` would stay the same. – Broxzier Aug 22 '13 at 12:04
  • 10
    Even more generally: `console.log('File1NotFoundTBA'.replace(/([^A-Z])([A-Z])/g, '$1 $2'));`. – Peter Behr Mar 15 '17 at 02:39
  • 5
    Should be noted the examples are not camel case which would have the first letter lower case. If this is expected to produce more of a title from true camel case string then you will still need to uppercase the first letter with one more line: `str[0].toUpperCase() + str.substring(1);` – racamp101 Jun 28 '19 at 17:37
  • 1
    Although this answers the OPs question, it doesn't handle `ThisIsASlug` or `Test1Test2`. You would get results like `This Is ASlug` and `Test1Test2` instead of `This Is A Slug` and `Test1 Test2`. – kimbaudi Mar 14 '21 at 20:52
  • 1
    how does it work though, we only specify $1 and $2 and it is working even when we have many many camel case examples. – Exploring Aug 20 '21 at 01:55
  • @Exploring the /g is the so called global flag, it means every match of said pattern will be replaced, not just first one. – PeterG Jun 09 '22 at 21:54
  • Nice but the string is PascalCase .__. – JsonKody Sep 29 '22 at 13:29
49
"MyCamelCaseString".replace(/([a-z](?=[A-Z]))/g, '$1 ')

outputs:

"My Camel Case String"
Adrian Salazar
  • 5,279
  • 34
  • 51
28

If you want an array of lower case words:

"myCamelCaseString".split(/(?=[A-Z])/).map(s => s.toLowerCase());

If you want a string of lower case words:

"myCamelCaseString".split(/(?=[A-Z])/).map(s => s.toLowerCase()).join(' ');

If you want to separate the words but keep the casing:

"myCamelCaseString".replace(/([a-z])([A-Z])/g, '$1 $2')
brielov
  • 1,887
  • 20
  • 28
  • 1
    Wasn't aware regular expressions worked inside the split function. I used a join after the split in my case. Super simple. – dezinezync May 14 '16 at 09:30
  • Putting it all together to handle the more advanced cases: `'File1NotFoundTBA'.split(/(?<![A-Z])(?=[A-Z])/)` gives `[ 'File1', 'Not', 'Found', 'TBA' ]`. – Trevor Robinson Mar 13 '20 at 03:57
  • @TrevorRobinson - but `'ThisIsASlug'.split(/(?<![A-Z])(?=[A-Z])/)` gives `["This", "Is", "ASlug"]`. Would be better if it returned `["This", "Is", "A", "Slug"]` instead. Eugene's solution can handle this case. – kimbaudi Mar 14 '21 at 20:35
  • I wish it also split `ThisIsASlug` to `This Is A Slug`. Unfortunately, I'm getting `This Is ASlug` with this solution. – kimbaudi Mar 14 '21 at 20:39
  • @kimbaudi Then just use the original version above: `'ThisIsASlug'.split(/(?=[A-Z])/)`. – Trevor Robinson Mar 15 '21 at 17:30
  • @TrevorRobinson, using `'ThisIsASlug'.split(/(?=[A-Z])/)` gives me `["This", "Is", "A", "Slug"]`, but it doesn't work well for `ExampleID`. `'ExampleID'.split(/(?=[A-Z])/)` will return `["Example", "I", "D"]`. So I'm not going to use either. – kimbaudi Mar 15 '21 at 17:54
  • Good point. Eugene's answer is probably best if your definition of camel case allows uppercase acronyms: `'ThisIsASlugWithAnExampleID'.split(/([A-Z][a-z]+)/).filter(w => w)` – Trevor Robinson Mar 16 '21 at 18:31
21

Sometime camelCase strings include abbreviations, for example:

PDFSplitAndMergeSamples
PDFExtractorSDKSamples
PDFRendererSDKSamples
BarcodeReaderSDKSamples

And in this case the following function will work, it splits the string leaving abbreviations as separate strings:

function SplitCamelCaseWithAbbreviations(s){
   return s.split(/([A-Z][a-z]+)/).filter(function(e){return e});
}

Example:

function SplitCamelCaseWithAbbreviations(s){
   return s.split(/([A-Z][a-z]+)/).filter(function(e){return e});
}

console.log(SplitCamelCaseWithAbbreviations('PDFSplitAndMergeSamples'));
console.log(SplitCamelCaseWithAbbreviations('PDFExtractorSDKSamples'));
console.log(SplitCamelCaseWithAbbreviations('PDFRendererSDKSamples'));
console.log(SplitCamelCaseWithAbbreviations('BarcodeReaderSDKSamples'));
kimbaudi
  • 13,655
  • 9
  • 62
  • 74
Eugene
  • 2,820
  • 19
  • 24
  • 3
    This is by far the most thorough. The filter is to remove the empty strings. You should add [0-9] on the lower case stuff, but this is definitely the final answer. The others fail on abbreviations in the middle of the string. – RoboticRenaissance Jun 10 '19 at 15:04
5

I found that none of the answers for this question really worked in all cases and also not at all for unicode strings, so here's one that does everything, including dash and underscore notation splitting.

let samples = [
  "ThereIsWay_too  MuchCGIInFilms These-days",
  "UnicodeCanBeCAPITALISEDTooYouKnow",
  "CAPITALLetters at the StartOfAString_work_too",
  "As_they_DoAtTheEND",
  "BitteWerfenSie-dieFußballeInDenMüll",
  "IchHabeUberGesagtNichtÜber",
  "2BeOrNot2Be",
  "ICannotBelieveThe100GotRenewed. It-isSOOOOOOBad"
];

samples.forEach(sample => console.log(sample.replace(/([^[\p{L}\d]+|(?<=[\p{Ll}\d])(?=\p{Lu})|(?<=\p{Lu})(?=\p{Lu}[\p{Ll}\d])|(?<=[\p{L}\d])(?=\p{Lu}[\p{Ll}\d]))/gu, '-').toUpperCase()));

If you don't want numbers treated as lower case letters, then:

let samples = [
  "2beOrNot2Be",
  "ICannotBelieveThe100GotRenewed. It-isSOOOOOOBad"
];

samples.forEach(sample => console.log(sample.replace(/([^\p{L}\d]+|(?<=\p{L})(?=\d)|(?<=\d)(?=\p{L})|(?<=[\p{Ll}\d])(?=\p{Lu})|(?<=\p{Lu})(?=\p{Lu}\p{Ll})|(?<=[\p{L}\d])(?=\p{Lu}\p{Ll}))/gu, '-').toUpperCase()));
Richtopia
  • 360
  • 2
  • 12
5

If you want to capitalize and add space between numbers as well, this works.

transform(value: string, ...args: any[]): string {
    const str = 'this1IsASampleText';
    str.charAt(0).toUpperCase() + value.slice(1); // Capitalize the first letter
    str.replace(/([0-9A-Z])/g, ' $&'); // Add space between camel casing
}

Results:

This 1 Is A Sample Text    
Kegan VanSickle
  • 239
  • 5
  • 3
3

Hi I saw no live demo , thanks @michiel-dral

var tests =[ "camelCase",
             "simple",
             "number1Case2",
             "CamelCaseXYZ",
             "CamelCaseXYZa" 
           ]

function getCamelCaseArray(camel) {
  var reg = /([a-z0-9])([A-Z])/g;
  return camel.replace(reg, '$1 $2').split(' ');
}

function printTest(test) {
document.write('<p>'+test + '=' + getCamelCaseArray(test)+'</p>');
}

tests.forEach(printTest);
<!DOCTYPE html>
<html>

  <head>
    <link rel="stylesheet" href="style.css">
    <script src="script.js"></script>
  </head>

  <body>
  </body>

</html>
Exploring
  • 2,493
  • 11
  • 56
  • 97
3

Regex not-a word boundary \B character can also be used

console.log("MyCamelCaseString".replace(/(\B[A-Z])/g, ' $1'));
alzed
  • 39
  • 4
  • but `'ExampleID'.replace(/(\B[A-Z])/g, ' $1')` gives me `Example I D`. i wish it gave me `Example ID` instead. – kimbaudi Mar 14 '21 at 20:47
2
a = 'threeBlindMice'
a.match(/[A-Z]?[a-z]+/g) // [ 'three', 'Blind', 'Mice' ]

is the simplest way I've found, for simple camel/titlecase splitting.

matt
  • 43
  • 4
2

If you're like me and had a camelCase value such as:

thisIsMyCamelCaseValue where the first letter is lowercased

function fromCamelCase(value) {
    const spaced = value.replace(/([a-z])([A-Z])/g, '$1 $2');
    return spaced.charAt(0).toUpperCase() + spaced.slice(1);
}
Luke Garrigan
  • 4,571
  • 1
  • 21
  • 29
0

I prefer to work with arrays over strings. It's easier to debug and more flexible. This is an actual join instead of replace. I haven't dealt with white spaces in the strings but you could just trim each element easily enough.

const splitCamelCase = str => str.match(/^[A-Z]?[^A-Z]*|[A-Z][^A-Z]*/g).join(' ');

console.log(splitCamelCase('fooMyCamelCaseString'));
console.log(splitCamelCase('MyCamelCaseString'));
console.log(splitCamelCase('XYZMyCamelCaseString'));
console.log(splitCamelCase('alllowercase'));
Rimian
  • 36,864
  • 16
  • 117
  • 117
  • 1
    but `'ExampleID'.match(/^[A-Z]?[^A-Z]*|[A-Z][^A-Z]*/g).join(' ')` gives me `Example I D`. I wish it would give me `Example ID` instead. – kimbaudi Mar 14 '21 at 20:45
0

You can use a combination of regEx, replace, and trim.

"ABCMyCamelCaseSTR".replace(/([A-Z][a-z0-9]+)/g, ' $1 ')
                   .replace(/\s{2}/g," ").trim()

// ABC My Camel Case STR
Behnam Azimi
  • 2,260
  • 3
  • 34
  • 52
  • since you added `0-9` into your regex, it would be nice to also provide an example string containing numeric characters (ex: `"ABCMy22Camel123CaseSTR"` returning `"ABC My22 Camel123 Case STR"`) – kimbaudi Mar 14 '21 at 20:29
  • 1
    also, your solution doesn't handle lowerCamelCase strings. `"ExampleID".replace(/([A-Z][a-z0-9]+)/g, ' $1 ').replace(/\s{2}/g," ").trim()` returns `Example ID`, but `exampleID` would still return `exampleID` instead of `example ID`. – kimbaudi Mar 14 '21 at 20:43
0

I recently came across this question and needed to do the exact same thing:

employeeID should be rendered as Employee ID

I found this convert case library from zellwk plus a little additional reduce function did the trick for me:

import { toTitle } from "./convert-case.js";

// NB. Assumes sequential single chars can be concatenated
// ex. N B A Finals => NBA Finals
const reducer = (total, currentValue, currentIndex, arr) => {
  if (
    currentValue.length === 1 &&
    !(currentIndex > 0 && arr[currentIndex - 1].length > 1)
  ) {
    return total + currentValue;
  } else {
    return total + " " + currentValue;
  }
};

const concatSingleChars = (title) => {
  const arrTitle = title.split(" ");
  return arrTitle.reduce(reducer);
};

const convertCase = (str) => {
  const s = toTitle(str);
  return concatSingleChars(s);
};

const tests = [
  "colName",
  "This_Is_A_title",
  "And_How_About_thisOne",
  "MaryHadALittleLamb",
  "employeeID",
  "N B A Finals",
  "N B A Finals in L A",
  "I Love L A"
];

const titles = tests.map((test) => {
  return convertCase(test);
});

console.log(titles);
Jordan
  • 159
  • 3
  • 14
0

Here is what I got from Github Copilot — the explanation is mine, this is not fully AI generated —:

  1. Put a space in front a capital letters via the /([A-Z])/g regex.
  2. Capitalize whatever is at the beginning of the string via the /^./ regex.
  3. Trim the leading and trailing white space with the .trim() method.

Here is the final code:

function camelCaseToCapitalized(str: string) {
  return str
    .replace(/([A-Z])/g, " $1")
    .replace(/^./, (str) => str.toUpperCase())
    .trim();
}
Philippe Fanaro
  • 6,148
  • 6
  • 38
  • 76
-4

This RegExp String is

.replace("/([a-zA-Z][a-z]*)/g",...);
stema
  • 90,351
  • 20
  • 107
  • 135
John
  • 1
  • 2