Vowels rhyme: Apples and Bananas

There is a traditional children's song that repeats the same text over and over, only each time every vowel is replaced with a random vowel, but constant across the current stanza.


The objective is to propose the shortest code that performs such a transformation on an input text.


  1. You must print the rhyme as many times as there are distinct vowels in it.
  2. Each print must be separated with a line break (platform-specific combo of \n and \r is accepted).
  3. For iteration i, replace each vowel with the ith distinct vowel in the original text.
  4. The input text is a sequence of printable ASCII characters (range [32, 126].
  5. Input will not contain embedded line breaks.
  6. Only vowels characters must be affected, other must be output exactly as input.
  7. Only vowels characters count: nasal vowels, although sounding like vowels (like in French "Tintin"), must not be handled as a single vowel.
  8. Case matters for the output, but is positional (replacing an uppercase vowel is done with the uppercase replacement vowel)
  9. Uppercase vowels are not distinct from their lowercase counterpart (ie a <=> A)
  10. Consecutive vowels are always considered separately (ie. Boat yields both Boot and Baat)
  11. Since the letter y represents either a vowel or consonant soun (as we're speaking English), handling it as a vowel or a consonant is allowed, however answers must explicitly state whether they handle y as a vowel or not.


Hello world

Hello world!


Helle werld!
Hollo world!

Excerpt from the original French text (translated), with y handled as a vowel:

An elephant that was rambling all gently in the woods...


An alaphant that was ramblang all gantla an tha waads...
En elephent thet wes rembleng ell gentle en the weeds...
In iliphint thit wis rimbling ill gintli in thi wiids...
Yn ylyphynt thyt wys rymblyng yll gyntly yn thy wyyds...
On olophont thot wos romblong oll gontlo on tho woods...

Note the behaviour on leading uppercase vowel: case is kept at its index (rules 8 and 9).

Vowelless example

Input that does not contain any vowel, like:

lgn@hst:~$ rm -rf ./* ~ /

must produce no output, or a single line break.

Single-vowel input

Input containing a single vowel is output as is.

Dad sat at a car and saw a fat bat.


Dad sat at a car and saw a fat bat.

This is , so fewest bytecount code wins (nothing but eternal PPCG glory)!


14 Answers 14


Retina, 45 bytes


Try it online! Does not count y as a vowel. Explanation:


Replaces the text with the literal string A\EI\OU.


Matches each letter optionally preceded by a backslash.


Outputs a line of Retina code for each letter.


Evaluates the generated code (shown below) using the original input. The . causes the code not to output the (final) buffer. The /<vowel>/i& causes the rest of the line to run only if the input contains the given vowel (case-insensitively). The * causes the result of the line to be ignored, so that the next vowel can be tested. The \ causes the result to be printed on its own line before it is ignored. The T`Vv`AAAAAa transliterates uppercase Vowels to AAAAAs and all lowercase vowels to a. \A is an escape that refers to ASCII 07 (BEL), but E, O and o are built-in character classes that needs to be escaped to give their literal values (e is not a character class, but fortunately it is also not an escape.)

  • \$\begingroup\$ Mmh, self-generating code. I don't know Retina well but that's impressive! \$\endgroup\$
    – joH1
    Sep 2, 2018 at 18:57
  • \$\begingroup\$ @joH1 Well, for me the impressive bit is that it saved 60 bytes! \$\endgroup\$
    – Neil
    Sep 2, 2018 at 19:19

Ruby, 78 bytes


Try it online!

A quick and naive approach. Y is not considered a vowel.


bash, 96 bytes

Two equal-length solutions:

v=aeiouAEIOU;for x in `grep -o [$v]<<<$1|sed 's/./\L&&&&&\U&/'|awk !a[\\$0]++`;{ tr $v $x<<<$1;}
v=aeiouAEIOU;for x in `tr -cd $v<<<$1|sed 's/./\L&&&&&\U&\n/g'|awk !a[\\$0]++`;{ tr $v $x<<<$1;}

Try it online!

Takes input as a command line argument and outputs to STDOUT.


05AB1E (legacy), 19 bytes

(Indirectly) saved one byte thanks to Kevin (printing directly inside the loop rather than joining, only work in the legacy version).


Try it online!

Using the Elixir rewrite, 20 bytes


Try it online! (without y) | Try it online! (with y, žM being replaced by žO – same applies for the legacy version)

How it works

lžMÃÙεžMDu«s5×Du«‡}»     Full program. Example: "Hello"
l                        Convert the input to lowercase. "Hello" –> "hello"
 žMÃ                     Keep only lowercase vowels. "hello" –> "eo"
    Ù                    Remove duplicates. "eo" –> "eo"
     ε            }      For each of the characters (example with "e"):
      žMDu«              Yield "aeiouAEIOU"
           s5×           Swap, and repeat the current char 5 times. "e" –> "eeeee"
              Du«        Duplicate, uppercase and merge. "eeeee" –> "eeeeeEEEE"
                 ‡       Transliteration. For each item in B, replace it in A with
                         the corresponding item in C.
                   »     Join on newlines.
  • \$\begingroup\$ Nice answer, shorter than the 21 byte answer I prepared. You can golf one more byte by looping and printing instead of mapping: 19 bytes. Your TIO with y should also use 6 instead of 5, btw. \$\endgroup\$ Sep 3, 2018 at 9:17
  • \$\begingroup\$ @KevinCruijssen Edited, thanks! About the y-vowel version, I accidentally copied the wrong TIO link when I answered :|... \$\endgroup\$
    – Mr. Xcoder
    Sep 3, 2018 at 9:22
  • \$\begingroup\$ Ah, now I know again why I had the i in my code.. Your answer fails for inputs without vowels. Expected is an empty output, but it actually prints the input itself.. :( \$\endgroup\$ Sep 3, 2018 at 9:31
  • 1
    \$\begingroup\$ @KevinCruijssen The 20-byter works, so I rolled back and fixed the second link. \$\endgroup\$
    – Mr. Xcoder
    Sep 3, 2018 at 9:34

Japt v2.0a0 -R, 24 22 bytes

Treats y as a vowel. Change both occurences of \y to \v to treat it as a consonant.

v f\y â £r\y_Xc^H*ZøZu

Try it


v                          :Lowercase
   \y                      :RegEx /[aeiouy]/gi (\v is /[aeiou]/gi)
  f                        :Get all matches as an array
      â                    :Deduplicate
        £                  :Map each X
         r\y               :  Replace all matches of the RegEx above in the input
             _             :  Pass matches through a function as Z
              Xc^          :    XOR the character code of X with
                 H*        :     32 multiplied by
                   Zø      :     Does Z contain
                     Zu    :      Uppercase Z
                           :Implicitly join with newlines and output

Jelly,  23 20 18  17 bytes

-2 Thanks to Erik the Outgolfer


To treat y as a vowel replace both cs with ys.

Try it online!


ØcŒHZx5fƇðØc,yð€Y - Link: list of characters, S
Øc                - vowels -> "AEIOUaeiou"
  ŒH              - split in half -> ["AEIOU", "aeiou"]
    Z             - transpose -> ["Aa", "Ee", "Ii", "Oo", "Uu"]
     x5           - times 5 -> ["AAAAAaaaaa", "EEEEEeeeee", "IIIIIiiiii", "OOOOOooooo", "UUUUUuuuuu"]
        Ƈ         - filter keep if:
       f          -   filter keep only -> those of X which have required vowels
                  -                       ...i.e. if S = "blah" then ["AAAAAaaaaa"]
         ð    ð€  - dyadic chain for €ach:
          Øc      -   vowels -> "AEIOUaeiou"
            ,     -   pair       e.g. ["AEIOUaeiou","AAAAAaaaaa"]
             y    -   translate  e.g. swap A for A, E for A, ...
                Y - join with newlines
  • \$\begingroup\$ 18 bytes (trivial) (ż/ on a pair is Z, two dyads in a row where the left one is unpaired in a monad have the argument in-between implicitly) \$\endgroup\$ Sep 2, 2018 at 19:40
  • \$\begingroup\$ Thanks (Z >_<), not sure what was going on with my TIO session, but removing the redundant ð wasn't working; restart fixed. \$\endgroup\$ Sep 2, 2018 at 19:54
  • \$\begingroup\$ TBH, I actually fixed x€x too, but you ninja'd me. :P \$\endgroup\$ Sep 2, 2018 at 19:55

Red, 229 bytes

Taking y for a non-vowel

func[s][v: charset"aoeiu"w: charset"AOEIU"p: copy""parse s[any[[copy c[v | w](if not find p c[append p c lowercase c
parse s[any[[copy n to[v | w | end]](prin n)opt[v(prin c)|[w(prin uppercase copy c)]| skip]]]print""])]| skip]]]

Try it online!

Slightly more readable:

f: func [ s ] [
    v: charset "aoeiu"
    w: charset "AOEIU"
    p: copy ""
    parse s[ 
        any [
            [ copy c [ v | w ]
                ( if not find p c [ 
                    append p c
                    lowercase c
                    parse s [
                        any [
                            [ copy n to [ v | w | end ] ]
                                 ( prin n )
                            opt [ v ( prin c )
                                | [ w ( prin uppercase copy c ) ]
                                | skip
                    print ""
                ] )
            | skip

R, 138, 129 bytes

function(x,U=utf8ToInt,a=U(V<-'aeiouAEIOU'))for(i in (which(a%in%U(x))-1)%%5)cat(chartr(V,intToUtf8(rep(a[i+c(1,6)],e=5)),x),'

Try it online!

  • y is not considered a vowel

Python, 129 119 112 bytes

import re
f=lambda s:'\n'.join(r('[AEIOU]',v.upper(),r('[aeiou]',v,s))for v in'aeiou'if v in s.lower());r=re.sub

Try it Online!

Doesn't treat \$y\$ as vowel.

-7 bytes thanks to @Mr.Xcoder

  • \$\begingroup\$ Glad to see you could get it working and golfed down a bit! Nice work \$\endgroup\$
    – joH1
    Sep 2, 2018 at 20:34
  • \$\begingroup\$ 112 bytes. \$\endgroup\$
    – Mr. Xcoder
    Sep 2, 2018 at 21:53

JavaScript (Node.js), 99 bytes

Treats \$y\$ as a consonant.


Try it online!


s => (                   // s = input string
  g = F =>               // g = helper function taking a callback function F
    Buffer(s)            // turn s into a Buffer
    .map(c =>            // for each ASCII code c in s:
      2130466            //   2130466 is a vowel bitmask: 1000001000001000100010
                         //                               u     o     i   e   a
      >> c               //   the ECMAScript specification enforces that the shiftCount is
                         //   the result of masking out all but the least significant 5 bits
      & c > 64           //   also make sure to ignore non-letter characters
      ?                  //   if a vowel is identified:
        F(c)             //     invoke F with c
      :                  //   else:
        c                //     just yield c
    ) + `\n`             // end of map(); coerce back to a string and append a newline
  )(v =>                 // invoke g with a callback that takes v:
    g[v &= 31] || (      //   unless this vowel has already been encountered:
      g[v] =             //     mark it as encountered
      S +=               //     and append to the output string S
      g(                 //     the result of another call to g:
        c => c & 96 | v  //       where vowels are replaced with v, using the original case
      )                  //     end of inner call to g
    ),                   //
    S = ''               //   start with S = ''
  ) && S                 // end of outer call to g; return S

Java 10, 196 188 bytes

s->{var d=new int[99];for(var c:s.toUpperCase().replaceAll("[^AEIOU]","").toCharArray())if(d[c]++<1)System.out.println(s.replaceAll("[AEIOU]",c+"").replaceAll("[aeiou]",(char)(c+32)+""));}

-8 bytes thanks to @joH1.

Without y as vowel to save bytes.

Try it online.


s->{                       // Method with String parameter and no return-type
  var d=new int[99];       //  Integer-array indicating which vowels we've already output
  for(var c:s.toUpperCase()//  Convert the input to uppercase
                           //  Remove all non-vowels
            .toCharArray())//  Convert it to a character array)
                           //  And loop over those vowel-characters
    if(d[c]++              //   Increase the vowel-count by 1
             <1)           //   And if it was 0 this iteration:
      System.out.println(  //    Print with trailing newline:
        s                  //     The input,
                           //     with every uppercase vowel replace with the current vowel
                           //     and every lowercase vowel replaced as well
  • \$\begingroup\$ 188 bytes by inlining the variable v in the loop \$\endgroup\$
    – joH1
    Sep 4, 2018 at 9:03
  • \$\begingroup\$ @joH1 Thanks, not sure how I missed that.. \$\endgroup\$ Sep 4, 2018 at 9:07

Python 2, 112 bytes

for v in V:
 if v in s.lower():print''.join([c,[v.upper(),v][c>'Z']][c in V]for c in s)

Try it online!

Treats y as a consonant.


Perl 6, 64 bytes

{m:g:i/<[aeiou]>/».lc.unique».&{$^v;say S:g:ii[<[aeiou]>]=$v}}

Try it online!


Java (OpenJDK 8), 184 bytes

y is a vowel


Try it online!


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.