Converting Between camelCase, snake_case, and kebab-case: Where Word-Splitting Goes Wrong

By · Published

Before a case converter can convert anything, it has to work out where one word ends and the next begins. For snake_case and kebab-case that's easy: split on _ or -. camelCase and PascalCase have no separator character, only a change from lowercase to uppercase, and that's where naive converters start making mistakes.

The two boundary rules

Word-splitting here runs two regular expressions in sequence. The first handles the ordinary case: a lowercase letter or digit followed by an uppercase letter is a boundary, so myVariable splits into my and Variable. The second handles acronyms. A run of two or more uppercase letters followed by an uppercase-then-lowercase pair is also a boundary, placed before the last capital in the run:

XMLParser
     ↓ (first rule finds no lowercase-to-uppercase transition — it's all caps until "arser")
     ↓ (second rule: "XMLP" is a run of capitals, followed by "Pa" — split before the "P")
XML Parser

Without the second rule, XMLParser would split at every capital and come out as x_m_l_parser instead of xml_parser. Placing the acronym boundary one letter too early or too late is the most common bug in hand-rolled case converters.

An acronym at the end of a word doesn't split

Advertisement

convertURL has one boundary: the lowercase t before the uppercase U. Nothing inside URL triggers a split, because no lowercase letter follows it to mark the end of the acronym. That gives the right answer, convert_url rather than convert_u_r_l. It also means a trailing acronym gets lowercased as one unit. Usually that's what you want. If a REST API wants URL capitalized in a constant name, you'll have to fix that by hand afterward.

Digits stay attached to their word

For boundary purposes a digit counts as a lowercase letter, not a category of its own. value2Count splits at the transition between 2 and C, giving value2 and Count. A digit sitting between two letters of the same case doesn't create a boundary by itself:

value2Count  →  value2 / Count   →  value2_count
item9        →  item9              →  item9
h2oLevel     →  h2o / Level        →  h2o_level

Where this matters: API boundaries

Most of the time you need this for translating field names across a boundary where the convention changes, not for style. The usual case is a Python or Ruby backend returning snake_case JSON keys to a JavaScript frontend that wants camelCase properties. A handful of fields you can convert by hand. With forty, you want a converter that gets acronyms and digits right every time, because one mis-split key turns into a property your code can't find, and nothing tells you.

Advertisement

Try the Case Converter →

Related tools & guides

Part of Encoding & Decoding Tools