Reverse the consonants

The program should take a string as input and reverse the consonants in it, while keeping the order of vowels. All the letters will be lowercase, so you don't have to worry about casing. Examples follow.

1. Input: a test case.
The consonants here are t,s,t,c,s. They should come in reversed order, i.e. s,c,t,s,t and inserted back into the string to the same positions where the pre-reversed characters were located: a sect sate.

2. Input: well-done. Output: nedl-lowe.

3. Input: reverse the consonants. Output: setenne sne cohtosarvr.

This is the code golf, the shortest solution wins.

Y should be considered vowel regardless of what it is and not reversed.

Anything, as well as numbers, punctuation, quotes, and other symbols (@#$%^&*(){}[]\|/<>~-_+=), could potentially appear in the input. 30 Answers Retina, 222120 17 O#^[b-z-[eiouy]]  Try it online! 1 byte thanks to Leaky Nun! 4 bytes thanks to Martin! O means sort, and # means to sort by numeric value. Since none of the matched characters will ever have a numeric value, all letters have the same weight: 0. ^ means to reverse the order of the sorted values, which thanks to stable sorting means the values are reversed. The -[...] means to do the setwise difference between the outer character class and this inner class. This is part of .NET and you can read more at the MSDN. • Very nice. Is -[...] specific to retina, or specific to .net regexes or is it a general regex feature that I've overlooked until now? – Digital Trauma Jun 17 '16 at 17:42 • @DigitalTrauma With this syntax I think it's specific to .NET. Other flavours have character class intersection as well, but then I think the syntax is usually [...&&[^...]]. – Martin Ender Jun 17 '16 at 17:44 • @MartinEnder I'll have to try these with sed and grep. Unlikely sed has it, but perhaps PCRE-mode grep. – Digital Trauma Jun 17 '16 at 17:52 • Perl 6 uses <[b..z] - [eiouy]> or <:Ll - [aeiouy]> to do set difference – Brad Gilbert b2gills Jun 18 '16 at 1:34 Python 2, 86 bytes s='';c=() for x in input():b='{'>x not in'aeiouy'<x;s+=b*'%s'or x;c=(x,)*b+c print s%c  Takes input as a string in quotes. Iterates through the input, replacing each consonant with %s in s. The tuple c stores the consonants encountered in reversed order. Then, string formatting replaces the %s's in s with the consonants in c. Thanks to Sp3000 for the consonant check, which saved 4 bytes over listing the consonants. • This is a beautiful bit of code golf :) – Lynn Jun 18 '16 at 11:06 • Really elegant, and surprisingly short, this being Python – DevilApple227 Jun 18 '16 at 20:03 Jelly, 22 20 bytes Øaḟ“<1Ṛż» e€¢œpżf¢Ṛ$


Try it online!

How it works

Øaḟ“<1Ṛż»   Helper link. No arguments.

Øa          Yield the lowercase alphabet/
“<1Ṛż»   Decompress that string, yielding "oui aye".
ḟ         Filter; remove the characters from the right string from the left one.

e€¢œpżf¢Ṛ$Main link. Argument: s (string) ¢ Call the helper link, yielding the string of all consonants. e€ Test each character of s for membership. œp Partition s at members/consonants.$  Combine the three links to the left into a monadic chain.
f¢    Filter by presence in the consonant string.
Ṛ   Reverse the result.
ż      Zipwith; interleave chunks of non-consonants and reversed consonants.


JavaScript ES6, 828180 78 bytes

Saved a byte thanks to each of Martin and Leaky Nun, and 2 bytes to Neil!

a=>a.replace(r=/(?[_aeiouy])\w/g,e=>[...a].reverse().join.match(r)[i++],i=0)


Testing

f=
a=>a.replace(r=/(?![aeiouy])[a-z]/g,e=>[...a].reverse().join.match(r)[i++],i=0)
;
q.onchange=q.onkeydown=q.onkeyup=function(){
o.innerHTML = "";
o.appendChild(document.createTextNode(f(q.value)));
}
*{font-family:Consolas,monospace;}
<input autocomplete="off" id=q>
<div id=o></div>

• That's the worse abuse of function arguments. – Leaky Nun Jun 17 '16 at 16:39
• /(?![aeiouy])[a-z]/g – Leaky Nun Jun 17 '16 at 16:40
• Would /(?[_aeiouy])\w/g work? – Neil Jun 17 '16 at 17:07
• @Neil No, that would match _, which is a symbol the OP wants to keep in place. – Conor O'Brien Jun 17 '16 at 17:10
• In your demo, you can change q.onchange=q.onkeydown=q.onkeyup= to just q.oninput=... – Patrick Roberts Jun 17 '16 at 21:10

Python 2, 106 bytes

s=input()
b=[x for x in s if x in'bcdfghjklmnpqrstvwxz']*2
print''.join(x in b and b.pop()or x for x in s)


Expects input in "quotes", which I think is allowed.

Pyke, 18 bytes

FD~c{IK_#~c{)oR@(s


Try it here!

or 16 bytes with the new version:

(Change so if for returns all string output and had string input, return string instead of a list)

FD~c{IK_#~c{)oR@


Try it here!

~c contains the consonants: bcdfghjklmnpqrstvwxz

F                - For i in input:
~c{I           -  if i in "bcdfghjklmnpqrstvwxz":
_#~c{)    -    filter(reversed(input), <-- in "bcdfghjklmnpqrstvwxz")
oR@ -   ^[o++]


GNU sed, 73

Score includes +1 for the -r flag passed to sed.

:
s/([b-df-hj-np-tv-xz])(.*)([b-df-hj-np-tv-xz])/\u\3\2\u\1/
t
s/./\l&/g


Ideone.

Repeatedly switches the first and the last lowercase consonant and converts them to upper case, until there are no more matches. Then convert the whole string back to lowercase.

J, 53 bytes

C.~[:~.<@~.@/:~"1@(,.|.)@I.@e.&'bcdfghjklmnpqrstvwxz'


Maybe the not the best way but I wanted to use C. since this can be solved using permutations.

Usage

   f =: C.~[:~.<@~.@/:~"1@(,.|.)@I.@e.&'bcdfghjklmnpqrstvwxz'
f 'a test case'
a sect sate
f 'well-done'
nedl-lowe
f 'reverse the consonants'
setenne sne cohtosarvr


Explanation

C.~[:~.<@~.@/:~"1@(,.|.)@I.@e.&'...'  Input: s
'...'  The list of consonants
e.&'...'  Generate a boolean mask where an index is true
if the char at that index in s is a consonant
I.@          Get a list of the true indices
(  |.)@             Reverse that list
(,.  )@             Join the original and reversed list as columns
/:~"1@                    Sort each row of that 2d list
~.@                          Take the distinct values in each row
<@                             Box them
[:~.                               Take the distinct boxes - Now represents the
permutation needed to solve this in cycle notation
C.~                                   Permute s according the cycles and return


MATL, 1822 21 bytes

tt2Y211Y2'y'hX-m)P5M(


1 byte saved thanks to @Luis

Unfortunately the longest part of this is getting the list of consonants (2Y211Y2'y'hX-).

Try it Online!

Explanation

            % Implicitly grab the input
tt          % Duplicate twice
2Y2         % Grab the pre-defined list of all lower-case letters
llY2        % Grab the pre-defined list of lower-case vowels (excluding 'y')
'y'h        % Add 'y' to the list of vowels
X-          % Find the set difference between these two lists (the consonants)
m           % Create a boolean array the size of the input that is true for all consonants
)           % Use this as an index to grab all consonants
P           % Reverse the order of the consonants
5M          % Get the boolean array again
(           % Re-assign the flipped consonants with the original consonant locations
% Implicitly dislpay the result

• 1 byte fewer: tt2Y211Y2'y'hX-m)P5M( – Luis Mendo Jun 18 '16 at 0:37
• @LuisMendo Excellent idea, thanks! – Suever Jun 18 '16 at 0:39

Perl 5 (58 + 4 = 62 bytes)

$_=<>;$r=qr/(?![eiouy])[b-z]/;@a=/$r/g;s/$r/pop@a/ge;print


+4 penalty for running with -0777 flag, which sets Perl to slurp mode to handle newlines correctly.

Accepts input through STDIN and prints to STDOUT.

Explanation

                   $_=<>; Read the input$r=qr/(?![eiouy])[b-z]/;   Save the regex; we'll be using it twice
@a=/$r/g; Store all the consonants in an array s/$r/pop@a/ge;   Replace each consonant with the final value in the array and pop
print    Output the result


JavaScript (ES6), 72 bytes

s=>s.split(/([^\W\d_aeiouy])/).map((c,i,a)=>i&1?a[a.length+~i]:c).join


Splitting on /([^\W\d_aeiouy])/ results in the consonants falling in the odd-numbered entries in the array. It then suffices to switch those entries with the equivalent entry counting back from the end of the array and join the result together.

• Nice job! I didn't think about splitting. – Conor O'Brien Jun 17 '16 at 19:32
• The CIA appears to have infiltrated your code. – Patrick Roberts Jun 17 '16 at 21:18
• I tried the same regexp but it reverse digits too – edc65 Jun 18 '16 at 14:27

JavaScript (ES6), 57 70

Edit Amazing 20% saving thx @Neil

Late to the party, but it seems all javascript people missed something

a=>a.replace(r=/[^\W\d_aeiouy]/g,_=>c.pop(),c=a.match(r))


Test

f=a=>a.replace(r=/[^\W\d_aeiouy]/g,_=>c.pop(),c=a.match(r))

function test() {
var i=I.value
O.textContent=f(i)
}

test()
#I { width:90% }
<input id=I oninput="test()" value="reverse the consonants."><pre id=O></pre>

• @nicael it's simply out of place (nitpick!) (but thanks for noticing) – edc65 Jun 18 '16 at 10:58
• As if it wasn't bad enough that (due to an error in my solution) you're beating me already, it seems that you should be able to save 13 bytes by using c.pop(). – Neil Jun 18 '16 at 17:23
• @Neil wow thanks that's a huge improvement – edc65 Jun 18 '16 at 23:52

Perl 5, 9268 55 bytes

Saved 37 bytes thanks to @manatwork's help. ;-)

$_=<>;@b=@a=/[^\Waeiou]/g;print$_~~@b?pop@a:$_ for/./g  A translation of @Lynn Python solution to Perl. • Hello, and welcome to PPCG! – NoOneIsHere Jun 18 '16 at 3:54 • @NoOneIsHere Sorry, what is PPCG. – aries Jun 18 '16 at 4:01 • Programming Puzzles & Code Golf. – NoOneIsHere Jun 18 '16 at 4:03 • Some “g” force would help there: @s=split//;@s=/./g; and for(@s){push@a,$_ if(/[^\Waeiou]/);}@a=/[^\Waeiou]/g;. I mean, m// with g modifier returns an array of all matches. – manatwork Jun 19 '16 at 12:16
• The 2nd version could be reduced by joining the assignments: @b=@a=…. Also the for having a single statement in the block (in which case the ; is not necessary after it), you could transform it into statement modifier and spare the delimiters: print$_~~@b?pop@a:$_ for/./g. (Yepp, sorry, missed that: no need to store @s value in a variable.) – manatwork Jun 20 '16 at 10:16

Pyth, 262524 23 bytes

s.i:Q++$J-G"aeiouy"$3_@J
J-G"aeiouy"sm?@dJ@_@JQ~hZ     <-- just keeping this because of the @_@
J-G"aeiouy"sm?@dJ@@JQ=tZ
sm?@dJ-G"aeiouy"@@JQ=tZ
sm|-dJ-G"aeiouy"@@JQ=tZ


Test suite.

Julia, 53 bytes

!s=s[flipud(i)]=s[i=find(c->'}'>c∉"aeiouy"<"$c",s)]  This takes a character array as input and reverses its consonants in-place. Try it online! Credit goes to @Sp3000 for the lowercase consonant check. How it works i=find(...,s) yields all indices of s for which the predicate ... returns true and saves them it the variable i. c->'}'>c∉"aeiouy"<"$c" performs three tests and returns true if and only if all are positive.

• '}'>c checks if the character c comes before {.

• "aeiou" checks if the string c comes after a.

• c∉"aeiouy" verifies that c is not a vowel.

Finally, s[i] yields all consonants and s[flipud(i)]=s[i] assigns them to positions in s that correspond to the reversed indices in in i.

• What encoding does this use (∉)? – Adám Jun 18 '16 at 22:38
• UTF-8, unfortunately. – Dennis Jun 18 '16 at 23:33

Java, 319305261 188 bytes

Credit to @Leaky Nun for helping with this :-)

char[]r(char[]s){int i=0,j=0,n=s.length;char[]o=new char[n];for(;i<n;i++){if((s[i]+"").matches("(?![eiouy])[b-z]")){o[j++]=s[i];s[i]=65;}}for(i=0;i<n;i++)if(s[i]==65)s[i]=o[--j];return s;}


Old:

s(String p){int i=0,j=0;char[]s=p.toCharArray(),o=p.toCharArray();for(;i<s.length;i++){if(((s[i]+"").matches("[aeiouy @#$%^&*(){}\$\$\\|/\\\\<>~\\-_+=]")))continue;o[j++]=(s[i]);s[i]='A';}for(i=0;i<s.length;i++)if(s[i]=='A')s[i]=o[--j];return new String(s);}  Inspiration taken from here Ungolfed String s(String p){ int i = 0, j = 0; char[]s=p.toCharArray(),o=p.toCharArray(); for (;i<s.length;i++) { if (((s[i]+"").matches("[aeiouy @#$%^&*(){}\$\$\\|/\\\\<>~\\-_+=]"))) continue;
o[j++] = (s[i]); // Store the consonant into o
s[i] = 'A'; // Put a special character in its place
}
for (i=0;i<s.length;i++)
if (s[i] == 'A') // If special character
s[i] = o[--j]; // Put back the consonant in reverse order
return new String(s);
}

• You can use 0 as the special character (null is guaranteed not to be in the String), and you can check it by s[i]<1 (there is no negative char) – Leaky Nun Jun 18 '16 at 3:10
• I will char you to pieces. :) – gcampbell Jun 18 '16 at 9:57

Ruby, 53 50 bytes

-3 bytes from @manatwork

->s{i=0;s.gsub(r=/[^\Waeiouy_]/){s.scan(r)[i-=1]}}


Try it here

• Why has the code block an unused parameter? – manatwork Jun 17 '16 at 22:23
• @manatwork is right. Ruby will allow you to omit the unused parameter since it's a block. You could shave off three characters there. – Silvio Mayolo Jun 18 '16 at 3:19
• @manatwork, I was initially going to use it for something, but then I didn't and as a result forgot to remove it – Value Ink Jun 18 '16 at 18:26

Python 2, 10398 100 bytes

import re
def r(s):a=re.split("([^\W\d_aeiouy])",s);print''.join(sum(zip(a[::2],a[-2::-2]+['']),()))


Port of my JavaScript answer. Edit: Saved 5 bytes thanks to @Dennis♦, of which I promptly had to spend two fixing digits.

R, 120 bytes

az=function(x){
y=strsplit(x, NULL)[[1]]
a=regexpr("[bc-df-hj-np-tv-z]", y)
y[a==1]=rev(y[a==1])
paste(y, collapse="")
}


takes a character string as x

az("reverse the consonants")
[1] "setenne sne cohtosarvr"


Old response below (110 bytes) was poor form on my part, which just reversed the consonants:

xrev=function(x){y=rev(strsplit(x, NULL)[[1]])
paste(y[is.na(match(y, c("a", "e","i","o","u","y")))], collapse="")}

• Why is the punctuation reversed? And where are the vowels gone? – nicael Jun 19 '16 at 7:03
• – Sumner18 Mar 18 at 15:55

APLX, 31 bytes

(c/t)←⌽t/⍨c←(t←⍞)∊⎕a~'aeoiuy'
t


⎕a~'aeoiuy' lowercase alphabet without vowels
t←⍞ store character input as t
c←()∊ store Boolean "consonant?" as c
t/⍨ extract (consonants) from t
⌽ reverse
(c/t)← replace consonants with (the reversed ones)
t return the modified string

Python 2.7, 144 bytes

def f(a):c='bcdfghjklmnpqrstvwxz';b=[x for x in list(a[::-1])if x in c];[b.insert(x,a[x])for x in range(len(a))if a[x]not in c];return''.join(b)


This first builds a reversed list of the consonants, then inserts each of the other characters back in at their original index.

Un-golfed:

s = 'well-done'
reverse = list(s[::-1])
consonants = [i for i in reverse if i in 'bcdfghjklmnpqrstvwxz']

for x in range(len(s)):
if s[x] not in 'bcdfghjklmnpqrstvwxz':
consonants.insert(x,s[x])

print(''.join(consonants))


https://repl.it/C30O

• You can save bytes by making a variable for 'bcdfghjklmnpqrstvwxz' and calling that variable instead – MCMastery Jun 19 '16 at 18:17

Mathematica 216 bytes

Module[{h,v,i},v=Characters["aeiouy "];h[s_]:=SortBy[Flatten[Thread/@Transpose@{v,#[[All,1]]&/@(StringPosition[s,#]&/@v)},1],Last];i[s_,{a_,n_}]:=StringInsert[s,a,n];Fold[i,StringReverse@StringReplace[#,v->""],h@#]]&


k="bcdfghjklmnpqrstvwxz";f c((r:q),s)=if celemk then(q,r:s)else(r:q,c:s);f c("",s)=("",c:s);g s=snd$foldr f(filter(elemk)s,"")s  Update @atlasologist's solution made me realize I only need a list of the consonants instead of pairs (no need to reverse them though since I'm using right fold.) Ungolfed consonants = "bcdfghjklmnpqrstvwxz" -- Combining function (right fold, improved) f :: Char -> (String, String) -> (String, String) f c ((r:rest), s) = if c elem consonants then (rest, r:s) else (r:rest, c:s) f c ("", s) = ("", c:s) transform :: String -> String transform s = snd$ foldr f (filter (elem consonants) s, "") s

main = do
line <- getLine
putStrLn . transform $line  Old c="bcdfghjklmnpqrstvwxz";n c(a@((f,t):q),s)=if c==f then(q,t:s)else(a,c:s);n c([],s)=([],c:s);g s=let z=filter(elemc)s in snd$foldr n(zip (reverse z)z,"")s


Creates a list of pairs of consonants, then walks through the string replacing each consonant using said list.

A bit primitive, but I wanted to figure this out without looking at the answers first. :)

• Some tips: a) use guards instead of if ... then ... else. b) f is better written as an infix operator, say %. c) no need for the inner pair of () in ((r:q),s). d) replace "" with _ in the 2nd line of f. All in all (k stays the same): c%(r:q,s)|celemk=(q,r:s)|1<2=(r:q,c:s);c%(_,s)=("",c:s);g s=snd$foldr(%)(filter(elemk)s,"")s. – nimi Jun 20 '16 at 20:26 s-lang, 17 16 bytes (non-competing) Saved one byte because s-lang no longer requires last argument bracket Try it online! r[(?![aeiouy])\w  I started working on a string manipulation golfing language (I have been wanting to do this for a time now), and I thought this would be a fun question to work on it with. Explanation: • r reverses the string with a given regex character matcher (if no regex argument is given, it will default to .) • [ begins the optional regex argument for r • (?![aeiouy])\w the regex to match any consonant character excluding y (unfortunately JavaScript doesn't allow character class subtraction) • ] usually ends optional regex argument for r, but we don't need it since it is the last function and last argument Matlab, 67 chars For an input 'this is a string of- stuff.' s=input('','s');si=regexp(s,'[b-df-hj-np-tv-xz]');s(si)=s(flip(si))  produces s = ffit is a fgnirt os- ssuht. si is the indices of the consonants in the input string. The final statement replaces those characters with the same characters but in reverse order by indexing. • This isn't going to handle any punctuation except -. The question says that any punctuation is valid input and should be ignored. Also, you'll need to either use input('') to grab the input or write an anonymous function because we can't accept a variable as input like this. – Suever Jun 17 '16 at 18:44 • Also you can use flip to reverse the string. – Suever Jun 17 '16 at 18:48 • Punctuation was edited into the question after I posted, but I'll fix that. As regards the input, can I just use ans, as that's what a default input to the Matlab console is assigned to? – sintax Jun 17 '16 at 18:48 • I don't think so. it should be a self-contained solution. You would need to do s=input('') or somehow craft this into an anonymous function @(s) – Suever Jun 17 '16 at 18:51 • Ok, I'll do that later or tomorrow. I'm away from my computer now. – sintax Jun 17 '16 at 19:03 PowerShell, 81 bytes -join(($a=$args|% t*y)|%{if($_-in($c=$a-match'[^\W\d_aeiouy]')){$_=$c[--$i]};$_})


Try it online!

Less golfed:

$a =$args|% toCharArray
$consonants =$a-match'[^\W\d_aeiouy]'
$result =$a|%{if($_-in$consonants){$_=$consonants[--$i]};$_}
-join($result)  PowerShell, 88 bytes, -f $i=0;-join($args|% t*y|%{if($_-match'[^\W\d_aeiouy]'){$c=,$_+$c;$_="{$i}";$i++};$_})-f$c


Try it online!

q/kdb+, 45 bytes

Solution:

{x[w]:x(|)w:(&)max x=/:.Q.a except"aeiouy";x}


Explanation:

Find indices of the consonants, and replace them with the reversed consonants:

{x[w]:x reverse w:where max x=/:.Q.a except "aeiouy";x} / ungolfed
{                                                   ; } / two-statement lambda
.Q.a except "aeiouy"    / alphabet (a..z) except vowels
x=/:                        / equals each-right (returns boolean lists where input is each a consonant)
max                             / 'or' the results together
where                                 / indices where results are true
w:                                      / save in variable w
reverse                                         / reverse this list
x                                                 / index into input at these indices
x[w]:                                                  / assign values to x at indices w
x  / return x


Notes:

I had 3 ways to build the consonant list, the one in the solution is slightly better than the alternatives:

• "bcdfghjklmnpqrstvwxz" for 22 chars (most boring)
• .Q.a _/0 3 6 11 16 19 for 21 chars (kinda cool, drop each index)
• .Q.a except"aeiouy" for 19 chars (second most boring)

Jq 1.5, 289 263 bytes

def C:"bcdfghjklmnpqrstvwxz";. as$s|[("^","")as$p|[match("([\($p)\(C)]+)";"g").captures[].string]]|.[1]|=(reduce map(length)[]as$l({s:add|explode|reverse|implode,r:[]};.r+=[.s[0:$l]]|.s=.s[$l:])|.r)|if$s[0:1]|inside(C)then[.[1,0]]else . end|transpose|map(add)|add  Explanation def consonants:"bcdfghjklmnpqrstvwxz"; . as$s
| [  ("^","") as $p # parse string onto non-consonant | [match("([\($p)\(consonants)]+)";"g").captures[].string]]   # and consonant groups
| .[1] |= (
reduce map(length)[] as $l ( # build new for consonant groups {s:add|explode|reverse|implode,r:[]} # by adding groups from reversed string ; .r+=[.s[0:$l]] | .s=.s[$l:] ) |.r ) | if$s[0:1]|inside(consonants) then  [.[1,0]] else . end        # put groups into proper order for merge
| transpose | map(add) | add                                     # merge groups into final string


Sample run

$jq -MRr 'def C:"bcdfghjklmnpqrstvwxz";. as$s|[("^","")as$p|[match("([\($p)\(C)]+)";"g").captures[].string]]|.[1]|=(reduce map(length)[]as$l({s:add|explode|reverse|implode,r:[]};.r+=[.s[0:$l]]|.s=.s[$l:])|.r)|if$s[0:1]|inside(C)then[.[1,0]]else . end|transpose|map(add)|add' input
a sect sate
nedl-lowe
setenne sne cohtosarvr


Try it online

Java 8, 157 bytes

s->s.format(s.replaceAll("[^\\W\\d_aeiouy]","%s"),s.chars().mapToObj(c->""+(char)c).filter(c->c.matches("[^\\W\\d_aeiouy]")).reduce("",(x,y)->y+x).split(""))


Try it online!

Note: prints compiler warnings to stderr.
Explanation:

s->                                                    // Lambda function
s.format(                                          // Format a string using the given arguments
s.replaceAll("[^\\W\\d_aeiouy]","%s"),         // Generate the format string by replacing all consonants with "%s"
s.chars()                                      // Stream the characters of the input string s
.mapToObj(c->""+(char)c)                   // Map characters back to strings
.filter(c->c.matches("[^\\W\\d_aeiouy]"))  // Filter out vowels and symbols
.reduce("",(x,y)->y+x)                     // Build the string of consonants in reverse
.split("")                                 // Turn into an array of single-char strings (varargs abuse)
)                                                  // Implicit return of the formatted string


Not gonna lie, my only goal was to beat this answer

APL (Dyalog Extended), 22 bytes

⌽@(('aeiou'~⍨∘⌊⎕A)∊⍨⊢)


Try it online!