# Coding Convention Conversion

In that Coding Golf, you should convert one coding convention with TitleCase to lower_case_with_underscores. And... vice versa!

# Specification

Change the casing in a following way:

• If underscore character is a delimiter, change the casing to Title Case without any of delimiter.
• If there are multiple words with no delimiter, change the casing to lower case and add an underscore character as a delimiter.
• In case of only one word (or one character): change the casing to Title Case if the word starts with lower case; change the casing to lower case if the word starts with the upper case.

Allowed characters:

• A to Z
• a to z
• underscore (_).

Input with mixed cased words are disallowed. Examples of disallowed cases:

• Coding_Convention_Conversion
• a_BC

# Example Cases

Input                        | Expected Output
===========================================================
CodingConventionConversion   | coding_convention_conversion
coding_convention_conversion | CodingConventionConversion
abc                          | Abc
Abc                          | abc
ABC                          | a_b_c
a_b_c                        | ABC
a                            | A
A                            | a


# Rules

• It is allowed to use ToUpper, ToLower and ToTitleCase functions.
• Using regular expressions is allowed.
• : shortest code in bytes wins!
• Is using a ToTitleCase function okay? You didn't specify, so I'd assume it is okay. Jan 26 '16 at 6:55
• @Justin: Good question indeed. Let's make it more fun and disallow the ToTitleCase function :) Jan 26 '16 at 7:04
• Darn... my solution relies on it Jan 26 '16 at 7:06
• @Justin: Okay - I didn't specify it in the beginning, so in that case - let's allow it anyway. Jan 26 '16 at 7:08

# Jolf, 35 bytes

Saves 1 byte thanks to @Cᴏɴᴏʀ O'Bʀɪᴇɴ. This is encoded in ISO 8859-7.

? hI'_ΜGI'_dpyH0pxRGIL0"(?=[A-Z])'_


Woohoo my first Jolf program!

## Explanation

   // I = input
? hI'_                              // If input contains _
GI'_                          // Split on _
Μ    d                         // Loop, then join
pyH0                     // Make the first character uppercase
// ELSE...
RGIL0"(?=[A-Z])    // Split *after* all uppercase chars
'_  // join with _
px                   //Make lowercase


Try it online

• You can use string separation at the end, so it becomes "(?=[A-Z])'_. The string is closed automatically. Jan 26 '16 at 0:54
• @CᴏɴᴏʀO'Bʀɪᴇɴ oh, cool, thanks! Jan 26 '16 at 0:55

# Retina, 37

Thanks to @ MartinBüttner for saving 4 bytes!

^|[A-Z]
_$0 TLllL_. ^_|_(?=[A-Z])  (Note the trailing newline.) Try it online. Note this includes extra m  to configure a couple of lines to treat each input line separately so all testcases may be run in one go. This is not a requirement of the question, so these are not counted in the score. • Lines 1 and 2 insert _ either at the beginning of input or before uppercase letters. All words are now _-separated, regardless of case. • Line 3 swaps case of the first letter in each word. • Lines 4 and 5 remove _ either at the start of input, or when followed by an uppercase letter. • This saves four bytes: retina.tryitonline.net/… Jan 26 '16 at 7:43 • Also you can avoid the trailing empty line by omitting the last ?= and replacing that stage with $1 (doesn't affect the byte count though). Jan 26 '16 at 7:44
• @Martin Very good - thanks! Jan 26 '16 at 16:11

# GNU Sed, 46

Thanks to @TobySpeight for saving 2 bytes!

Score includes +1 for -E (or -r) option to sed.

s/(^|_)([a-z])/\u\2/g
t
s/[A-Z]/_\l&/g
s/^_//


Try it online.

Fairly straightforward sed:

• Line 1 substitutes beginning of line or _, followed by a lowercase letter with the upper case of that letter. The g flag to s performs this substitution for each instance found
• t jumps to the : unnamed label if there were any matches for the above substitution. This label is implicitly at the end.
• Otherwise all uppercase letters are substituted with _ the lower case of that letter
• This leaves a leading _ before the first letter. s/^_// removes that.
• @Toby Thanks. -E works in my GNU sed 4.2.2 (Ubuntu 14.04.3), though its not in the man page. I read somewhere [citation needed] that -E is newer Posix option that will officially be added to GNU Sed in a newer release, but is already there unofficially. Regardless, -r does the right thing if -E doesn't work for you. Jan 26 '16 at 16:19
• @Toby lines 280-282 of sed/sed.c are /* Undocumented, for compatibility with BSD sed. */ case 'E': case 'r':. Jan 26 '16 at 17:00
• @Digital - I was mistaken; my sed does accept -E as a synonym for -r. I wasn't correctly passing a minimal program e.g. sed -E -e Q Jan 26 '16 at 18:03

# Pyth, 25 bytes 29 33 35 40

Saved 2 bytes thanks to @Dennis

Saved 4 bytes thanks to @FryAmTheEggman

?rIz0smrd4cz\_tsXzrG1*\_G


Try it online

• Your link needs to be updated. Jan 26 '16 at 6:36
• When I try put "abc" as an input, it yields "bc" as an output. Bug? :) Jan 26 '16 at 9:39
• To fix what @DariuszWoźniak noticed, you can change your condition from /z\_ to rIz0. I also believe I found a same-length alternative to the adding underscores program: tsXzrG1_Mcj\_G2, maybe someone can golf it more... Jan 26 '16 at 18:16
• Ah, found it: tsXzrG1*\_G Jan 26 '16 at 18:29

# JavaScript (ES6), 87 bytes

s=>s.replace(/[A-Z]|(^|_)(.)/g,(c,_,l,i)=>l?l.toUpperCase():(i?"_":"")+c.toLowerCase())


## Explanation

Depending on which part of the regex matched, it replaces the match with the opposite case.

s.replace(
/[A-Z]|(^|_)(.)/g,
(c,_,l,i)=>
l?
(i?"_":"")+c.toLowerCase()
:l.toUpperCase()
)


## Test

var solution = s=>s.replace(/[A-Z]|(^|_)(.)/g,(c,_,l,i)=>l?l.toUpperCase():(i?"_":"")+c.toLowerCase())
<input type="text" id="input" value="coding_convention_conversion" />
<button onclick="result.textContent=solution(input.value)">Go</button>
<pre id="result"></pre>

# PHP 160 bytes

not the shortest but for completeness here my solution in PHP, $s holds the string to convert: trim(preg_replace_callback('/((^[a-z]|_[a-z])|([A-Z]))/',function($m){return empty($m[2])?'_'.strtolower($m[3]):strtoupper(str_replace('_','',$m[2]));},$s),'_')

• Welcome to Programming Puzzles and Code Golf Stack Exchange. Well done for posting something in a language that you knew wasn't going to win. code-golf challenges are mostly within languages, so using a non-golfing language is good. +1 d:-D Jan 28 '16 at 17:04

# Ruby, 10187 75 bytes

->s{s.gsub(/^.|[A-Z]/,'_\0').gsub(/_./,&:swapcase).gsub(/_(?=[A-Z])|^_/,'')}


Unfortunately, this does exactly the same thing as the Retina solution, as that method ended up being shorter than anything else I came up with.

# Python 3, 130 bytes

Quick and dirty attempt using regex to split at the caps. Pretty brute force: if anyone can come up with a different approach I'm sure this can be beaten.

import re
lambda s:('_'.join(re.findall('[A-Z][a-z]*',s)).lower(),''.join([a[0].upper()+a[1:]for a in s.split('_')]))[s.islower()]


# 𝔼𝕊𝕄𝕚𝕟 3, 15 chars / 32 bytes (noncompetitive)

⟮ѨǱï⟯≠ï?Ⅰ:ѨȎѨƎï


Try it here (Firefox only).

v3 was released after this challenge, with a bunch of bugfixes and library updates.

# Explanation

This is just a mashup of builtins.

⟮ѨǱï⟯≠ï?Ⅰ:ѨȎѨƎï // implicit: ï=input
⟮ѨǱï⟯≠ï?        // check if ï is NOT in snake_case
Ⅰ       // if so, then convert to snake_case
:ѨȎѨƎï // otherwise, convert to camelCase and make the first letter UPPERCASE


e€ØAḊkŒlj”_$ʋ⁾_ yŒtḲƲ}Ẹ?  Try it online! # Python 3, 86 bytes lambda s,u='_':''.join([u[i>u:]+i.lower()for i in(s<u)*s]or[u]+s.title().split(u))[1:]  Try it online! Also works in Python 2. Making use of the convenient fact that the ascii value for _ (95) is right in between those of the uppercase (65-90) and lowercase (97-122) letters, which allows for easy string comparisons. # Perl 6, 73 72 71 68 bytes {.comb(/<:Lu><:Ll>*|<:Ll>+/).map({/<:Lu>/??.lc!!.tc}).join('_'x?/<:Lu>/)} # 73 {.comb(/<:Lu><:Ll>*|<:L>+/).map({/<:Lu>/??.lc!!.tc}).join('_'x?/<:Lu>/)} # 72 {/<:Lu>/??S:g/(^)?(<:Lu>)/{$0||'_'}$1.lc()/!!S:g/[^|_](<:Ll>)/$0.tc()/}   # 71
{.comb(/<:Lu><:Ll>*|<:L>+/).map({/<:Lu>/??.lc!!.tc}).join('_'x!/_/)}      # 68


### Usage:

# give it a lexical name
my &code = {...}

for <CodingConventionConversion coding_convention_conversion abc Abc ABC a_b_c a A>
{ say .&code }

coding_convention_conversion
CodingConventionConversion
Abc
abc
a_b_c
ABC
A
a


### Explanation:

{
.comb( / <:Lu><:Ll>* | <:L>+ / ) # grab the "words" only
.map({
/<:Lu>/ # if the word has uppercase
??
.lc     # lowercase the whole word
!!
.tc     # otherwise titlecase the word
})
.join(  # join the words
'_'   # with '_'
x     # repeated
!/_/  # zero times if it had a _, otherwise once
)
}


You may be wondering why I used the Unicode properties (<:Lu>, <:Ll>) instead of just a character class. In Perl 6 they are no longer spelled [a-z] they are spelled <[a..z]> which is 1.6 times as big. The brackets [ … ] are used for non-capturing grouping instead which was spelled as (?: … ) in Perl 5.

# Japt, 40 bytes

UfV="%A" ?UrV@'_s!Y +Xv} :Ur"^.|_."_sJ u


Test it online!

# How it works

           // Implicit: U = input string
UfV="%A"   // Set variable V to the string "\\A", and get all matches in U.
?          // If the list is not null:
UrV@     } //  Replace each match X and its index Y with this function:
'_s!Y +Xv  //   Return "_".slice(!Y) (1 for Y=0, 0 for anything else) + X.toLowerCase().
:          // Otherwise:
Ur"^.|_."  //  Replace the char at the beginning and each char following an underscore with:
_sJ u      //   The last char of the match (the letter) .toUpperCase().

• Why is the U not automatically inserted? May 28 at 4:41
• @SamuelWaller, because this solution pre-dates that feature being added. May 31 at 15:28

# Perl 5, 42 bytes

40 bytes plus 2 for -p (thanks, dev-null)

s/[A-Z]/_\l$&/g||s/(^|_)(.)/\u$2/g;s/_//

• On Windows, using perl and MINGW32, I get no output, what am I missing? Jan 27 '16 at 21:46
• @ChatterOne I don't know what MINGW32 is, but it worked fine for me on Strawberry Perl. Use -E instead of -e. Jan 27 '16 at 21:49

# Forth (gforth), 129 bytes

: f bounds dup c@ 32 xor emit 1+ ?do i c@ '_ < if ." _"i c@ 32 + emit then i c@ '_ > if i 1- c@ '_ = 32 * i c@ + emit then loop ;


Try it online!

### Code Explanation

: f              \ start a new word definition
bounds         \ convert string address and length to beginning and ending address
dup c@         \ get the first character
32 xor emit    \ convert to the opposite case and output
1+             \ add 1 to beginning of string (skip starting char)
?do            \ begin counted loop over string character addresses
i c@ '_ <    \ check if char is uppercase
if           \ if it is:
." _"      \ output underscore
i c@       \ get current char
32 + emit  \ convert to lowercase and output
then         \ end if block
i c@ '_ >    \ check if lowercase (not '_')
if           \ if it is:
i 1- c@    \ get the previous character
'_ = 32 *  \ if it's an underscore, multiply by 32 (true = -1 in forth)
i c@ +     \ add result to current char (make uppercase if previous was '_')
emit       \ output resulting char
then         \ end if block
loop           \ end loop
;                \ end word definition



# Pip, 37 bytes

'_Na?UC@_.@>_Ma^'_(LC_MRXU.XL.'+a)J'_


Try it online!

Lot of underscores in this one.

# K (ngn/k), 43 bytes

{("_"/(&x<_x)__x;,/c$t-32*~<'t:"_"\x)x~_x}  Try it online! • x~_x check if the input x is all lowercase • (...;...) index into this two-item list depending on that value (i.e., return the first item in the list if x contains an uppercase letter, and the second item if it does not) • When x contains an uppercase letter: • (&x<_x) identify indices of uppercase letters • (...)__x split the lowercased input on those indices • "_"/ join the resulting chunks with underscores • When x is all lowercase: • t:"_"\x split input on underscores, storing in variable t • t-32*~<'t capitalize the first letter in each chunk (this converts to integers) • ,/c$ convert back to characters, flattening the list

# Japt, 37 bytes

a'_ Ä?Uq'_ ®hZu g} q:Uò@Yc <#a} q'_ v


Try it online!

Explanation:

There are some unicode chars that a shortcuts for common groups of chars,
like Ä is +1. This is the expanded version.

// U = input string
a'_  // get index of "_" in string, -1 if not found
+1   // add 1 to make -1 falsy
?    // if truthy, ie. if there is an underscore in the input text
Uq'_ // split on underscores
m_   // map
h    // replace character (first char by default)
Zu g // the first letter of the string converted to uppercase (Zug)
} q  // close function and join
:    // else, ie. the string doesn't have any underscores
Uò   // runs func on every pair of chars, splits them if it returns true
@Yc <#a } // returns true if the second char code is less than "a", ie. uppercase
q'_  // join with underscores
v    // make lowercase

implicit print


First Japt program, I beat the creator, and Zu g!

# Japt, 27 26 bytes

I fear I may have gone in the wrong direction with this; stuck on 27 and can't seem to do any better.

r"^.|_.|%A"ÈÌc^H i'_pY©XÊu


Try it

r"^.|_.|%A"ÈÌc^H i'_pY©XÊu     :Implicit input of string
r                              :Replace
"^.|_.|%A"                    :RegEx /^.|_.|[A-Z]/g
È                   :Pass each match X at 0-based index Y through the following function
Ì                  :  Last character of X
c                 :  Charcode
^H               :  Bitwise XOR with 32
i             :  Prepend
'_p          :    "_" repeated
Y©        :      Logical AND of Y and
XÊ      :      Length of X
u     :      Mod 2