# 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. – Justin Jan 26 '16 at 6:55
• @Justin: Good question indeed. Let's make it more fun and disallow the ToTitleCase function :) – Dariusz Woźniak Jan 26 '16 at 7:04
• Darn... my solution relies on it – Justin 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. – Dariusz Woźniak Jan 26 '16 at 7:08

# 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. – isaacg Jan 26 '16 at 6:36
• When I try put "abc" as an input, it yields "bc" as an output. Bug? :) – Dariusz Woźniak 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... – FryAmTheEggman Jan 26 '16 at 18:16
• Ah, found it: tsXzrG1*\_G – FryAmTheEggman Jan 26 '16 at 18:29

# 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. – Conor O'Brien Jan 26 '16 at 0:54
• @CᴏɴᴏʀO'Bʀɪᴇɴ oh, cool, thanks! – Downgoat 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/… – Martin Ender 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). – Martin Ender Jan 26 '16 at 7:44
• @Martin Very good - thanks! – Digital Trauma 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. – Digital Trauma 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':. – Digital Trauma 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 – Toby Speight Jan 26 '16 at 18:03

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

# 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()]


# 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 – wizzwizz4 Jan 28 '16 at 17:04

# 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().  # 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? – ChatterOne 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. – msh210 Jan 27 '16 at 21:49 # 𝔼𝕊𝕄𝕚𝕟 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  # Jelly, 24 bytes 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.

# 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