25
\$\begingroup\$

Input

A string of printable ASCII characters, for example:

This is an example string.

Output

For every consonant (BCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz) that is not followed by a vowel (AEIOUaeiou) add the last vowel before it, in lowercase.
Consonants before the first vowel are left as-is:

Thisi isi ana examapale seterinigi.

Test cases

AN EXAMPLE WITH A LOT UPPERCASE (plus some lowercase)
=> ANa EXAMaPaLE WITiHi A LOTo UPuPEReCASE (pelusu some lowerecase)

And here comes a **TEST** case with 10% symbols/numbers(#)!
=> Anada here comese a **TESeTe** case witihi 10% siyimiboloso/numuberese(#)!

This is an example string.
=> Thisi isi ana examapale seterinigi.

abcdefghijklmnopqrstuvwxyz
=> abacadefegehijikiliminopoqorosotuvuwuxuyuzu

A pnm bnn
=> A panama banana

Tell me if you need more test cases!
=> Telele me ifi you neede more tesete casese!

Scoring

As this is , the answer with the lowest byte-count in each language wins (no answer will be accepted).

\$\endgroup\$
  • \$\begingroup\$ So, should the inserted vowels be always lowercase, while the text might be both uppercase and lowercase? \$\endgroup\$ – Erik the Outgolfer Jun 6 '18 at 17:53
  • \$\begingroup\$ Can the output be in the form of a list/array? \$\endgroup\$ – Nathan Dimmer Jun 6 '18 at 17:54
  • \$\begingroup\$ @EriktheOutgolfer Yes, I did not want uppercase where we need lowercase, but it would have overcomplicated the challenge if one had to check the case of adjacent letters \$\endgroup\$ – wastl Jun 6 '18 at 17:55
  • 11
    \$\begingroup\$ Eat healthy kids, try A pnm bnn! \$\endgroup\$ – Stewie Griffin Jun 6 '18 at 20:09
  • 4
    \$\begingroup\$ Does anyone else think "How Italians" needs to go in the title somewhere? \$\endgroup\$ – Artelius Jun 7 '18 at 1:17

19 Answers 19

14
\$\begingroup\$

Retina, 48 bytes

i`(?<=([aeiou]).*?[^\W\d_aeiou])(?![aeiou])
$l$1

Try it online! Explanation: The lookahead searches for a point not followed by a vowel, while the lookbehind searches for an immediately preceding consonant and a previous vowel, which is then inserted in lower case.

\$\endgroup\$
8
\$\begingroup\$

JavaScript (ES6), 108 105 bytes

(Saved 3 bytes thanks to @Shaggy.)

f=s=>(t=(s+=' ').replace(/[aeiou]|[a-z][^aeiou]/ig,r=>r[1]?r[0]+v.toLowerCase()+r[1]:v=r,v=''))!=s?f(t):s

Searches for vowels or for consonants with no following vowel:

/[aeiou]|[a-z][^aeiou]/ig

(We don't need to search for consonants explicitly, because vowels are excluded based on the /[aeiou]|....)

Vowels are stored in v, and consonants with no following vowel have v inserted:

r[1]?r[0]+v.toLowerCase()+r[1]:v=r

(If r[1] exists, we've matched on a consonant plus non-vowel.)

If nothing has been changed, we return the input. Otherwise, we recurse on the replaced string.

f=s=>(t=(s+=' ').replace(/[aeiou]|[a-z][^aeiou]/ig,r=>r[1]?r[0]+v.toLowerCase()+r[1]:v=r,v=''))!=s?f(t):s

console.log(f('A pnm bnn'));
console.log(f('abc'));
console.log(f('AN EXAMPLE WITH A LOT UPPERCASE (plus some lowercase)')); 
console.log(f('And here comes a **TEST** case with 10% symbols/numbers(#)!'));
console.log(f('This is an example string.'));
console.log(f('abcdefghijklmnopqrstuvwxyz'));
console.log(f('Tell me if you need more test cases!'));

\$\endgroup\$
  • 1
    \$\begingroup\$ This is even better. I really need to look at regex \$\endgroup\$ – Luis felipe De jesus Munoz Jun 6 '18 at 19:45
  • \$\begingroup\$ +1 smart idea to use replace over map + join \$\endgroup\$ – Downgoat Jun 6 '18 at 19:56
  • \$\begingroup\$ Based of yours an (almost) working version without recursion: s=>s.replace(/[aeiou][^a-z]*([a-z](?![aeiou]))+/gi,s=>s.replace(/(?!^)./g,a=>a+s[0].toLowerCase())) I can't seems to have issues with sequences of non-letters though \$\endgroup\$ – Downgoat Jun 6 '18 at 20:06
  • \$\begingroup\$ Recursion certainly simplifies things here. \$\endgroup\$ – Rick Hitchcock Jun 6 '18 at 20:14
  • \$\begingroup\$ (s+=' ') should save a few bytes. \$\endgroup\$ – Shaggy Jun 6 '18 at 22:01
5
\$\begingroup\$

Python 2, 134 119 bytes

def f(s,v=''):u=s[:1];return s and u+v*(u.isalpha()-g(u)-g((s+u)[1]))+f(s[1:],[v,u.lower()][g(u)])
g='aeiouAEIOU'.count

Try it online!

EDIT: 15 bytes thx to Lynn

\$\endgroup\$
  • \$\begingroup\$ I fiddled with it a little for 119 bytes. \$\endgroup\$ – Lynn Jun 7 '18 at 4:59
  • \$\begingroup\$ @Lynn: Love the <vowels>.count. \$\endgroup\$ – Chas Brown Jun 7 '18 at 5:32
4
\$\begingroup\$

Standard ML, 225 223 bytes

str o Char.toLower;fun?c=String.isSubstring(it c)"aeiou"fun g(x,l)$d=(fn l=>if Char.isAlpha$andalso not(?d)then if? $then(it$,l)else(x,l^x)else(x,l))(l^str$)fun f$(c::d::r)=f(g$c d)(d::r)|f$[c]= #2(g$c c);f("","")o explode;

Try it online!

Less golfed:

val lower = str o Char.toLower

fun isVowel c = String.isSubstring (lower c) "aeiou"

(* c is the current char, d is the next char, x is the last vowel and l the accumulator 
   for the resulting string *)
fun g (x,l) c d = 
    if Char.isAlpha c andalso not (isVowel d)
    then if isVowel c 
         then (lower c, l^str c)
         else (x, l^str c^x)
    else (x, l^str c)

fun f t (c::d::r) = f (g t c d) (d::r)
  | f t [c] = #2(g t c #"d")

val h = f ("","") o explode;

Try it online!

\$\endgroup\$
  • \$\begingroup\$ wow, ML golf looks really interesting! I love it and the use of the $ variable name. \$\endgroup\$ – Lynn Jun 7 '18 at 5:50
  • \$\begingroup\$ @Lynn I wrote a tip about identifier renaming some time ago and planned to write one about it as well, but haven't gotten around doing so yet. \$\endgroup\$ – Laikoni Jun 7 '18 at 13:50
4
\$\begingroup\$

sed 4.2.2, 64 bytes

:
s/(([aeiou])[^a-z]*[b-df-hj-np-tv-z])([^aeiou]|$)/\1\l\2\3/I
t

Try it online!

\$\endgroup\$
  • \$\begingroup\$ I'll be honest, my goal here is just to try to beat perl by a couple of bytes. Let's see if it holds :) \$\endgroup\$ – KernelPanic Jun 7 '18 at 8:36
4
\$\begingroup\$

Perl 5, 68 67 59 bytes

perl -pe '$v="[aeiou])";1while s/($v[^a-z]*[b-z]\K(?<!$v(?!$v/\L$1/i'

Here's a great example of the usefulness of \K, and I can't believe I didn't know about this feature before Dom Hastings pointed it out.

I haven't been able to get the right behavior with just using s///g, so an actual loop seems necessary. (It's possible that the right use of a look-behind assertion could work without an explicit while -- but I haven't found it.)

\$\endgroup\$
  • \$\begingroup\$ Nice approach! Wasn't able to come up with anything better, but managed to get 6 bytes off: Try it online! \$\endgroup\$ – Dom Hastings Jun 7 '18 at 10:08
  • 1
    \$\begingroup\$ @DomHastings: Even shorter (down to 58 bytes) by factoring out [aeiou]) to variable: Try it online! \$\endgroup\$ – ShadowRanger Jun 7 '18 at 19:26
3
\$\begingroup\$

JavaScript ES6, 115 bytes

Saves 8 bytes thanks to @ETHProductions

s=>[x="",...s].map((i,j)=>(r=/[aeiou]/i).test(i)?x=i:/[a-z]/i.test(i)&&!r.test(s[j]||1)?i+x.toLowerCase():i).join``

I have managed to inflate this more in the process of golfing it O_o but it also fixes a bug

s=>[x="",...s].map(             // Create a new array with x storing last vowel
                                // This also offsets indexes by one so rel to original str refers to next char
   (i,j)=>                      // Going through each char...
      (r=/[aeiou]/i).test(i)?   // If it's vowel, store it in x
          x=i:
      /[a-z]/i.test(i)          // If a letter (thats not a vowel excluded by above)
         &&!r.test(s[j]||1)?    // Test if next char is *not* vowel
         i+x.toLowerCase():i    // If it isn't, then add the most recent vowel after
    ).join``                    // Combine back to string
\$\endgroup\$
  • \$\begingroup\$ @RickHitchcock oh shoot totally forgot about ending char, fixing asap \$\endgroup\$ – Downgoat Jun 6 '18 at 19:42
  • 1
    \$\begingroup\$ @RickHitchcock ok fixed \$\endgroup\$ – Downgoat Jun 6 '18 at 19:55
  • \$\begingroup\$ Ah, yes thanks for the golf @ETHproductions \$\endgroup\$ – Downgoat Jun 6 '18 at 20:12
3
\$\begingroup\$

JavaScript, 88 82 Bytes

Done with a single regular expression:

Original Version (88 Bytes):

s=>s.replace(/(?<=([aeiou]).*?(?![aeiou])[a-z])(?=[^aeiou]|$)/gi,(_,c)=>c.toLowerCase())

Updated Version (82 Bytes) after looking at Neil's regular expression:

s=>s.replace(/(?<=([aeiou]).*?[^\W\d_aeiou])(?![aeiou])/gi,(_,c)=>c.toLowerCase())

var tests = {
  "AN EXAMPLE WITH A LOT UPPERCASE (plus some lowercase)":
    "ANa EXAMaPaLE WITiHi A LOTo UPuPEReCASE (pelusu some lowerecase)",
  "And here comes a **TEST** case with 10% symbols/numbers(#)!":
    "Anada here comese a **TESeTe** case witihi 10% siyimiboloso/numuberese(#)!",
  "This is an example string.":
     "Thisi isi ana examapale seterinigi.",
  "abcdefghijklmnopqrstuvwxyz":
    "abacadefegehijikiliminopoqorosotuvuwuxuyuzu",
  "A pnm bnn":
     "A panama banana",
  "Tell me if you need more test cases!":
     "Telele me ifi you neede more tesete casese!"
};

for ( test in tests )
{
  var result = (s=>s.replace(/(?<=([aeiou]).*?[^\W\d_aeiou])(?![aeiou])/gi,(_,c)=>c.toLowerCase()))(test);
  console.log( result === tests[test], result );
}

\$\endgroup\$
3
\$\begingroup\$

Japt -P, 28 bytes

ó@\ctX ©\VtYÃËè\v ?P=D:D¬qPv

Try it online!

Unpacked & How it works

UóXY{\ctX &&\VtY} mD{Dè\v ?P=D:Dq qPv

UóXY{           }  Split the string between any two chars that don't satisfy...
     \ctX &&\VtY     The first char is a consonant and the second is a non-vowel
mD{                And map...
   Dè\v              If this item is a vowel...
       ?P=D            Assign it to P and return as-is
           :Dq qPv     Otherwise, split the item into chars and join with P lowercased
                       (P starts with "", so beginning consonants are not affected)

-P                 Join with ""

The ó function wins over any kind of regexes.

\$\endgroup\$
  • \$\begingroup\$ Nice one, ya beat me :D. \$\endgroup\$ – Magic Octopus Urn Jun 12 '18 at 18:00
  • \$\begingroup\$ Nicely done - I gave myself severe brainache with this one! \$\endgroup\$ – Shaggy Jun 13 '18 at 15:05
2
\$\begingroup\$

JavaScript (Node.js), 146 143 132 127 125 bytes

(a,v="")=>[...a].map((b,i)=>(w=/[aeiou]/i).test(b)&&(v=b)?b:w.test(a[i+1]||b)||!b.match(/[a-z]/i)?b:b+v.toLowerCase()).join``

Try it online!

\$\endgroup\$
2
\$\begingroup\$

Perl 6,  75 73 71  69 bytes

{({S:i/.*(<[aeiou]>).*<-[\W\d_aeiou]><()><![aeiou]>/$0.lc()/}...*eq*).tail}

Try it

{({S:i{.*(<[aeiou]>).*<-[\W\d_aeiou]><()><![aeiou]>}=$0.lc}...*eq*).tail}

Try it

{({S:i{.*(<[aeiou]>).*<:L-[_aeiou]><()><![aeiou]>}=$0.lc}...*eq*).tail}

Try it

{({S:i{.*(<[aeiou]>).*<:L-[_aeiou]><(<![aeiou]>}=$0.lc}...*eq*).tail}

Try it

Expanded:

{  # bare block lambda with implicit parameter $_

  (
    # generate a sequence

    {  # code block used to generate the values

      S               # substitute (not in-place)
      :i              # :ignorecase
      {

          .*              # start at end of string

          ( <[aeiou]> )   # store the previous vowel in $0

          .*

          <:L - [_aeiou]> # letter other than a vowel

          <(              # ignore everything before this

                          # this is where `$0.lc` gets inserted

          # )>            # ignore everything after this

          <![aeiou]>      # not a vowel (zero width lookahead)

      } = $0.lc       # replace with lowercase of the earlier vowel
    }

    ...    # keep generating until:

    * eq * # there are two equal strings (no changes)

  ).tail   # get the last value
}
\$\endgroup\$
2
\$\begingroup\$

Python 3, 125 bytes

lambda s,v='[^aeiouAEIOU':sub(f'(?<={v}\W\d])(?={v}]|$)',lambda m:sub(f'{v}]','',s[:m.end()])[-1:].lower(),s)
from re import*

Try it online!

Python 3.6 allows us to (ab)use f-strings to reuse our set of vowels (and for four more characters saved, the beginning of an inverted regex character class) cheaply (a f prefix on each string, then {v} as needed, instead of the '+v+' you'd need with concatenation, or the [^aeiouAEIOU you'd insert literally.

The regex that matches no characters, just a position, avoids issues with the non-overlapping matches normal regexes require, and removes the need to backreference any part of the match; all we use the match object for is to get the slice index we use to find the prior vowel.

Partially de-golfed, it would be something like:

import re

def get_last_vowel(string):
    '''
    Returns the lowercase version of the last vowel in a string if
    the string contains any vowels, otherwise, return the empty string
    '''
    try:
        *restvowels, lastvowel = re.sub(r'[^aeiouAEIOU]', '', string)
    except ValueError:
        lastvowel = ''  # No vowels in string
    return lastvowel.lower()

def rememebere_tehe_vowelese(string):
    '''Inserts the lowercased last vowel seen after any consonant not followed by a vowel'''
    return re.sub(r'(?<=[^aeiouAEIOU\W\d])(?=[^aeiouAEIOU]|$)',
                  lambda match: get_last_vowel(string[:match.end()]),
                  string)
\$\endgroup\$
2
\$\begingroup\$

TSQL, 500 bytes

 CREATE TABLE i (i CHAR(999)); INSERT i VALUES ('The rain in Spain stays mainly in the plain')
 DECLARE @w CHAR(999)=(SELECT i FROM i),@r VARCHAR(999)='';WITH d(n,c,i,q)AS(SELECT n,SUBSTRING(@w,n,1),CHARINDEX(SUBSTRING(@w,n,1),'AEIOUaeiou'),CHARINDEX(SUBSTRING(@w,n,1),'BCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz')FROM(SELECT DISTINCT number n FROM master..[spt_values]WHERE number BETWEEN 1 AND LEN(@w))D)SELECT @r=@r+f.c+LOWER(COALESCE(CASE WHEN f.q<>0 AND COALESCE(d2.i,0)=0 THEN SUBSTRING(@w,(SELECT MAX(n)FROM d WHERE i<>0 AND n<f.n),1)END,''))FROM d f LEFT JOIN d d2 ON f.n=d2.n-1 SELECT @r

Table i is used for input

\$\endgroup\$
  • 2
    \$\begingroup\$ Assuming input to be present in a certain variable is generally not allowed. Can this be adapted to be a function instead? \$\endgroup\$ – Laikoni Jun 7 '18 at 18:56
  • \$\begingroup\$ @Laikoni solution updated to match given rules \$\endgroup\$ – Jan Drozen Jun 8 '18 at 7:47
2
\$\begingroup\$

SWI-Prolog, 593 bytes

a(S,D):-atom_chars(S,D).
g(_,[],_,-1).
g(E,[E|_],R,R).
g(E,[_|T],I,R):-N is I+1,g(E,T,N,R).
c(A,E):-g(E,A,0,R),R > -1.
v(X):-a('AEIOUaeiou',X).
c(X):-a('BCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz',X).
d([_],_,R,R).
d([H|T],_,I,R):-v(V),c(V,H),!,d(T,H,I,[H|R]).
d([H,N|T],V,I,R):-c(C),c(C,H),v(W),c(W,N),!,d([N|T],V,I,[H|R]).
d([H,N|T],V,I,R):-c(C),c(C,H),v(W),\+c(W,N),string_lower(V,LV),!,d([N|T],V,I,[LV,H|R]).
d([H|T],V,I,R):-!,d(T,V,I,[H|R]).
r([],Z,Z).
r([H|T],Z,A):-r(T,Z,[H|A]).
r(S,D):-r(S,D,[]).
m(X,R):-a(X,O),r(O,P),r([''|P],Q),d(Q,'',I,[]),r(I,J,[]),atomic_list_concat(J,R).

Used only built-in predicates (without regex or list manipulating library).

Usage:

?- m('A pnm bnn').
'A panama banana'
true .
\$\endgroup\$
2
\$\begingroup\$

Haskell, 142 130 bytes

""&
import Data.Char
v=(`elem`"aeiouAEIOU")
s&(x:y:z)|v y=x:s&(y:z)
s&(x:y)|v x=x:[toLower x]&y|isAlpha x=x:s++s&y|1>0=x:s&y
_&x=x

Try it online!

The initial ""& is a partial application of the (&) function defined later on, and is placed so oddly to make TIO count the bytes in ""&, but not count the bytes that, in a full program, would be needed to assign that to any named value.


Less golfed:

import Data.Char (isAlpha, toLower)

vowel :: Char -> Bool
vowel = (`elem`"aeiouAEIOU")

replace :: String -> String
replace = go "" -- start by carrying no extra vowel
  where go _ "" = ""
        -- special case for "anything followed by vowel" so later cases can ignore next character
        go s (x:y:more) | vowel y = x : go s (y:more)
        go s (x:xs) | vowel x = x : go [toLower x] xs -- update the vowel we're carrying
                    | isAlpha x = x : s ++ go s xs -- non-vowel letter not followed by a vowel
                    | otherwise = x : go s xs -- some non-letter junk, just include it and carry on

There really ought to be a way to do this more concisely with a fold instead of recursion, but I couldn't figure it out.

\$\endgroup\$
  • \$\begingroup\$ Here is a very hacky way of defining a header such that f does not appear in the body: Try it online! \$\endgroup\$ – Laikoni Jun 8 '18 at 4:50
  • \$\begingroup\$ There are two unnecessary spaces in v = ( and you can define g as an infix operator. \$\endgroup\$ – Laikoni Jun 8 '18 at 4:54
  • \$\begingroup\$ Putting the base case g _""="" in last position saves a byte: g _ x=x (two bytes if you switch to infix as Laikoni suggests). \$\endgroup\$ – nimi Jun 8 '18 at 20:01
  • \$\begingroup\$ Per our conventions you'd need to add parenthesis around ""& to make it an function. \$\endgroup\$ – Laikoni Jun 9 '18 at 20:24
1
\$\begingroup\$

05AB1E, 34 bytes

vyžMylåil©1V}žPylåžM¹N>èå_Y&&i®«}J

Try it online!


I take that back I can only shave 3 bytes off this monstrosity... I think I could shave the boolean down, but there MUST be 3 cases. 1 for vowels. 1 for consonants. 1 for the case that a digit/symbol exists.


v                                 # For each...
 y                                # Push current element.
  žM                              # Push lower-case vowels (aeiou).
    ylå                           # Lower-case current element is vowel?
       i©1V}                      # If so, put it in register, set Y to 1.
            žP                    # Push lower-case consonants (b...z)
              ylå                 # Is current char a consonant?
                 žM¹N>èå_         # Push vowels again, is input[N+1] NOT a vowel? 
                         Y        # Did we ever set Y as 1?
                          &&      # All 3 previous conditions true?
                            i®«}  # Concat the current vowel to the current char.
                                J # Join the whole stack.
                                  # '}' isn't needed here, b/c it's implied.
                                  # Implicit return.
\$\endgroup\$
0
\$\begingroup\$

Powershell, 104 bytes

based on Neil's regular expression.

[regex]::Replace($args,'(?i)(?<=([aeiou]).*?[^\W\d_aeiou])(?![aeiou])',{"$($args.Groups[1])".ToLower()})

save it as get-rememebere.ps1. Script for testing:

$test = @"
AN EXAMPLE WITH A LOT UPPERCASE (plus some lowercase)
And here comes a **TEST** case with 10% symbols/numbers(#)!
This is an example string.
abcdefghijklmnopqrstuvwxyz
A pnm bnn
Tell me if you need more test cases!
"@

$expected = @"
ANa EXAMaPaLE WITiHi A LOTo UPuPEReCASE (pelusu some lowerecase)
Anada here comese a **TESeTe** case witihi 10% siyimiboloso/numuberese(#)!
Thisi isi ana examapale seterinigi.
abacadefegehijikiliminopoqorosotuvuwuxuyuzu
A panama banana
Telele me ifi you neede more tesete casese!
"@

$result = .\get-rememebere.ps1 $test
$result -eq $expected
$result
\$\endgroup\$
0
\$\begingroup\$

Red, 276 bytes

func[s][v: charset t:"AEIOUaeiou"c: charset 
u:"BCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz"b:
parse s[collect[any keep[thru c opt v]keep thru end]]p:""foreach
c b[either find t e: last c: to-string c[p: e][parse c[any[copy p v
| skip]]if find u e[append c lowercase p]]prin c]]

Try it online!

Readable:

f: func [ s ] [
   v: charset t: "AEIOUaeiou"
   c: charset u: "BCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz"
   b: parse s [
       collect [ any keep [ thru c opt v ]
       keep thru end ]
   ]
   p: "" 
   foreach c b [
       e: last c: to-string c
       either find t e [ p: e ][
           parse c [ any [ copy p v | skip ] ]
           if find u e [ append c lowercase p ]
       ]
       prin c
   ]
]
\$\endgroup\$
0
\$\begingroup\$

Yabasic, 180 bytes

A full program which takes input from STDIN and outputs to STDOUT

Line Input""s$
x$="AEIOUaeiou"
For i=1To Len(s$)
c$=Mid$(s$,i,1)
?c$;
If InStr(x$,c$)Then
v$=c$
Else
a=Asc(Upper$(c$))
If a>64And a<91And!InStr(x$,Mid$(s$,i+1,1))Then?v$;Fi
Fi
Next

Try it online!

\$\endgroup\$

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.