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.


34 Answers 34


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\$ Jun 17, 2016 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\$ Jun 17, 2016 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\$ Jun 17, 2016 at 17:52
  • 1
    \$\begingroup\$ Perl 6 uses <[b..z] - [eiouy]> or <:Ll - [aeiouy]> to do set difference \$\endgroup\$ Jun 18, 2016 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, 2016 at 11:06
  • \$\begingroup\$ Really elegant, and surprisingly short, this being Python \$\endgroup\$ Jun 18, 2016 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, 2016 at 16:39
  • \$\begingroup\$ /(?![aeiouy])[a-z]/g \$\endgroup\$
    – Leaky Nun
    Jun 17, 2016 at 16:40
  • \$\begingroup\$ Would /(?[_aeiouy])\w/g work? \$\endgroup\$
    – Neil
    Jun 17, 2016 at 17:07
  • \$\begingroup\$ @Neil No, that would match _, which is a symbol the OP wants to keep in place. \$\endgroup\$ Jun 17, 2016 at 17:10
  • 1
    \$\begingroup\$ In your demo, you can change q.onchange=q.onkeydown=q.onkeyup= to just q.oninput=... \$\endgroup\$ Jun 17, 2016 at 21:10

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.


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

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, 2016 at 0:37
  • \$\begingroup\$ @LuisMendo Excellent idea, thanks! \$\endgroup\$
    – Suever
    Jun 18, 2016 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\$ Jun 17, 2016 at 19:32
  • 5
    \$\begingroup\$ The CIA appears to have infiltrated your code. \$\endgroup\$ Jun 17, 2016 at 21:18
  • \$\begingroup\$ I tried the same regexp but it reverse digits too \$\endgroup\$
    – edc65
    Jun 18, 2016 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, 2016 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, 2016 at 17:23
  • \$\begingroup\$ @Neil wow thanks that's a huge improvement \$\endgroup\$
    – edc65
    Jun 18, 2016 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\$ Jun 18, 2016 at 3:54
  • 1
    \$\begingroup\$ @NoOneIsHere Sorry, what is PPCG. \$\endgroup\$
    – aries
    Jun 18, 2016 at 4:01
  • \$\begingroup\$ Programming Puzzles & Code Golf. \$\endgroup\$ Jun 18, 2016 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, 2016 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, 2016 at 10:16

Ruby, 53 50 47 bytes

-3 bytes from @manatwork. -3 bytes from @Jordan.

->s{a=s.scan r=/[^\Waeiouy_]/

Attempt This Online!

  • \$\begingroup\$ Why has the code block an unused parameter? \$\endgroup\$
    – manatwork
    Jun 17, 2016 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\$ Jun 18, 2016 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, 2016 at 18:26
  • \$\begingroup\$ -2 bytes \$\endgroup\$
    – Jordan
    Nov 29, 2022 at 13:49

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, 2016 at 22:38
  • 1
    \$\begingroup\$ UTF-8, unfortunately. \$\endgroup\$
    – Dennis
    Jun 18, 2016 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, 2016 at 3:10
  • \$\begingroup\$ I will char you to pieces. :) \$\endgroup\$
    – gcampbell
    Jun 18, 2016 at 9:57

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="")}

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.


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

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


05AB1E, 11 bytes


I/O as a list of characters.

Try it online or verify all test cases.


.ā          # Enumerate the (implicit) input, pairing each character with its index
  žP        # Push the consonants constant (excluding 'y'): "bcdfghjklmnpqrstvwxz"
    I       # Push the input
     å      # Check for each character if it's in the consonants string
      Ï     # Keep the enumerated pairs at the truthy indices
       ø    # Zip/transpose; swapping rows/columns
        `   # Dump the list of consonants and list of indices separated to the stack
         R  # Reverse the list of indices
          ǝ # Insert the consonants at the (reversed) indices in the (implicit) input
            # (after which the result is output implicitly)
  • 1
    \$\begingroup\$ Note that "Y should be considered vowel ... and not reversed." \$\endgroup\$ Nov 30, 2022 at 23:43
  • \$\begingroup\$ @DominicvanEssen Ah, I misread that. Great to see a challenge where 'y' is a vowel, since it's usually the reversed. Should be fixed. \$\endgroup\$ Dec 1, 2022 at 7:42

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, 2016 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, 2016 at 20:26

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


APL (Dyalog Extended), 22 bytes


Try it online!

  • \$\begingroup\$ Note that "Y should be considered vowel ... and not reversed." \$\endgroup\$ Nov 30, 2022 at 23:44

Raku, 48 46 bytes

{my$r=/<:L-[aeiouy]>/;S:g[$r]=pop @||=m:g/$r/}

Try it online!

  • $r is set to a consonant-matching regex.
  • @ ||= m:g/$r/ stores the consonants in the input string in the anonymous state array @, if it's empty.
  • S:g[$r] = pop ... replaces each consonant with the consonant popped off the end of the array into which all of the consonants were stored initially.

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)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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