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 30


Retina, 22 21 20 17


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.

  • \$\begingroup\$ Very nice. Is -[...] specific to retina, or specific to .net regexes or is it a general regex feature that I've overlooked until now? \$\endgroup\$ – Digital Trauma Jun 17 '16 at 17:42
  • \$\begingroup\$ @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 [...&&[^...]]. \$\endgroup\$ – Martin Ender Jun 17 '16 at 17:44
  • \$\begingroup\$ @MartinEnder I'll have to try these with sed and grep. Unlikely sed has it, but perhaps PCRE-mode grep. \$\endgroup\$ – Digital Trauma Jun 17 '16 at 17:52
  • 1
    \$\begingroup\$ Perl 6 uses <[b..z] - [eiouy]> or <:Ll - [aeiouy]> to do set difference \$\endgroup\$ – Brad Gilbert b2gills Jun 18 '16 at 1:34

Python 2, 86 bytes

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.

  • 1
    \$\begingroup\$ This is a beautiful bit of code golf :) \$\endgroup\$ – Lynn Jun 18 '16 at 11:06
  • \$\begingroup\$ Really elegant, and surprisingly short, this being Python \$\endgroup\$ – DevilApple227 Jun 18 '16 at 20:03

Jelly, 22 20 bytes


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, 82 81 80 78 bytes

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



  o.innerHTML = "";
<input autocomplete="off" id=q>
<div id=o></div>

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

Python 2, 106 bytes

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


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)


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.



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


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


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


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, 18 22 21 bytes


1 byte saved thanks to @Luis

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

Try it Online!


            % 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
  • \$\begingroup\$ 1 byte fewer: tt2Y211Y2'y'hX-m)P5M( \$\endgroup\$ – Luis Mendo Jun 18 '16 at 0:37
  • \$\begingroup\$ @LuisMendo Excellent idea, thanks! \$\endgroup\$ – Suever Jun 18 '16 at 0:39

Perl 5 (58 + 4 = 62 bytes)


+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.


                   $_=<>;   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


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.

  • \$\begingroup\$ Nice job! I didn't think about splitting. \$\endgroup\$ – Conor O'Brien Jun 17 '16 at 19:32
  • 5
    \$\begingroup\$ The CIA appears to have infiltrated your code. \$\endgroup\$ – Patrick Roberts Jun 17 '16 at 21:18
  • \$\begingroup\$ I tried the same regexp but it reverse digits too \$\endgroup\$ – 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




function test() {
  var i=I.value

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

  • \$\begingroup\$ @nicael it's simply out of place (nitpick!) (but thanks for noticing) \$\endgroup\$ – edc65 Jun 18 '16 at 10:58
  • \$\begingroup\$ 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(). \$\endgroup\$ – Neil Jun 18 '16 at 17:23
  • \$\begingroup\$ @Neil wow thanks that's a huge improvement \$\endgroup\$ – edc65 Jun 18 '16 at 23:52

Perl 5, 92 68 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.

  • \$\begingroup\$ Hello, and welcome to PPCG! \$\endgroup\$ – NoOneIsHere Jun 18 '16 at 3:54
  • 1
    \$\begingroup\$ @NoOneIsHere Sorry, what is PPCG. \$\endgroup\$ – aries Jun 18 '16 at 4:01
  • \$\begingroup\$ Programming Puzzles & Code Golf. \$\endgroup\$ – NoOneIsHere Jun 18 '16 at 4:03
  • 1
    \$\begingroup\$ 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. \$\endgroup\$ – manatwork Jun 19 '16 at 12:16
  • 1
    \$\begingroup\$ 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.) \$\endgroup\$ – manatwork Jun 20 '16 at 10:16

Pyth, 26 25 24 23 bytes

J-G"aeiouy"sm?@dJ@_@JQ~hZ     <-- just keeping this because of the @_@

Test suite.


Julia, 53 bytes


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.

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

Java, 319 305 261 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;}


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


String s(String p){
    int i = 0, j = 0;
    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);
  • 2
    \$\begingroup\$ 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) \$\endgroup\$ – Leaky Nun Jun 18 '16 at 3:10
  • \$\begingroup\$ I will char you to pieces. :) \$\endgroup\$ – gcampbell Jun 18 '16 at 9:57

Ruby, 53 50 bytes

-3 bytes from @manatwork


Try it here

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

Python 2, 103 98 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

New answer:

y=strsplit(x, NULL)[[1]]
a=regexpr("[bc-df-hj-np-tv-z]", y)
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="")}

APLX, 31 bytes


⎕a~'aeoiuy' lowercase alphabet without vowels
t←⍞ store character input as t
c←()∊ store Boolean "consonant?" as c
t/⍨ extract (consonants) from t
(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.


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':



  • \$\begingroup\$ You can save bytes by making a variable for 'bcdfghjklmnpqrstvwxz' and calling that variable instead \$\endgroup\$ – 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@#]]&

Haskell, 157 131 bytes

k="bcdfghjklmnpqrstvwxz";f c((r:q),s)=if c`elem`k then(q,r:s)else(r:q,c:s);f c("",s)=("",c:s);g s=snd$foldr f(filter(`elem`k)s,"")s


@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.)


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


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(`elem`c)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. :)

  • 4
    \$\begingroup\$ 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)|c`elem`k=(q,r:s)|1<2=(r:q,c:s);c%(_,s)=("",c:s);g s=snd$foldr(%)(filter(`elem`k)s,"")s. \$\endgroup\$ – 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!


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.


  • 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.'


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.

  • \$\begingroup\$ 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. \$\endgroup\$ – Suever Jun 17 '16 at 18:44
  • 1
    \$\begingroup\$ Also you can use flip to reverse the string. \$\endgroup\$ – Suever Jun 17 '16 at 18:48
  • \$\begingroup\$ 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? \$\endgroup\$ – sintax Jun 17 '16 at 18:48
  • 1
    \$\begingroup\$ 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) \$\endgroup\$ – Suever Jun 17 '16 at 18:51
  • \$\begingroup\$ Ok, I'll do that later or tomorrow. I'm away from my computer now. \$\endgroup\$ – 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]};$_}

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


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


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


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


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:]                               
| 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
setenne sne cohtosarvr

Try it online


Java 8, 157 bytes


Try it online!

Note: prints compiler warnings to stderr.

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


Try it online!


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.