21
\$\begingroup\$

Task description

Sometimes, you really need to fit something you’re writing in a small space. It may be tempting to drop the vowels and wrt lk ths – and failing that, who really needs spaces? Thssprfctlrdbl!

Write a function or program that removes lowercase vowels aeiou, and then spaces, and then any characters from an input string. Furthermore, each time you remove a character, it must be the rightmost character eligible for removal. It must repeat this process until the string is no longer than some given input length.

† “This is perfectly readable!” But if you’re reading this footnote, it probably isn’t, really... :)

Examples

Here, you can see this process applied for successively smaller input sizes:

23: Hello, Code Golf World!
22: Hello, Code Golf Wrld!
21: Hello, Code Glf Wrld!
20: Hello, Cod Glf Wrld!
19: Hello, Cd Glf Wrld!
18: Hell, Cd Glf Wrld!
17: Hll, Cd Glf Wrld!
16: Hll, Cd GlfWrld!
15: Hll, CdGlfWrld!
14: Hll,CdGlfWrld!
13: Hll,CdGlfWrld
12: Hll,CdGlfWrl
11: Hll,CdGlfWr
(etc.)

After squeezing the string down to 17 characters, we run out of vowels to remove, so the next character we remove is the rightmost space; when we hit 14 characters, we’ve removed all vowels and spaces, so we simply start munching the string from right to left.

Here is some pseudocode Python code that solves this challenge:

def crunch_string(string, to_length):
    while len(string) > to_length:
        # Store the best candidate index for deletion here.
        best = None

        # First, find the rightmost vowel's index.
        for i in range(len(string)):
            if string[i] in 'aeiou':
                best = i

        # If there were no vowels, find the rightmost space's index.
        if best is None:
            for i in range(len(string)):
                if string[i] == ' ':
                    best = i

        # If there were no spaces either, use the final index.
        if best is None:
            best = len(string) - 1

        # Remove the selected character from the string.
        string = string[:best] + string[best + 1:]

    # Return the string once `len(string) <= to_length`.
    return string

Rules

  • This is , so the shortest code in bytes wins.

  • The input string will consist of the printable ASCII characters from space (, decimal 32) up to and including tilde (~, decimal 126). There will be no uppercase vowels AEIOU in the string. In particular, there will be no Unicode, tabs, or newlines involved.

  • Call the input string s, and the input target length t. Then 0 < t ≤ length(s) ≤ 10000 is guaranteed. (In particular, the input string will never be empty. If t = length(s), you should just return the string unmodified.)

Test cases

Input:  50, Duis commodo scelerisque ex, ac consectetur metus rhoncus.
Output: Duis commodo scelerisque ex, ac cnscttr mts rhncs.

Input:  20, Maecenas tincidunt dictum nunc id facilisis.
Output: Mcnstncdntdctmnncdfc

Input:  150, golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf
Output: glf glf glf glf glf glf glf glf glf glf glf glf glf glf glf glf glf glf glf glf glf glf glf glf glf glf glf glf glf glf glfglfglfglfglfglfglfglfglfglf
\$\endgroup\$
6
  • 5
    \$\begingroup\$ Is y a vowel ? \$\endgroup\$
    – edc65
    Feb 29, 2016 at 22:51
  • 1
    \$\begingroup\$ Can’t believe I forgot to explain that! No, aeiou are vowels, and AEIOU will not occur, for simplicity. (The whole uppercase/lowercase thing is not what I want to focus on.) I added clarification. \$\endgroup\$
    – Lynn
    Feb 29, 2016 at 22:53
  • 1
    \$\begingroup\$ Very nice challenge! \$\endgroup\$
    – Luis Mendo
    Feb 29, 2016 at 23:54
  • \$\begingroup\$ @edc65 Don't forget w (for example, in the word cow, w is a vowel!) Of course, that's settled for this one, but for where it's not stated that the set of vowels is aeiou, you should sometimes include y and w. :-O \$\endgroup\$
    – corsiKa
    Mar 1, 2016 at 20:01
  • \$\begingroup\$ Unrelated to golfing, but consider for index, char in enumerate(string) instead of the range(len(str)) construct \$\endgroup\$ Sep 28, 2017 at 20:25

12 Answers 12

11
\$\begingroup\$

Perl, 48 45 43 bytes

Includes +4 for -Xlpi (-X can be left out but leaves ugly warnings on STDERR)

Run with the number after the -i option and the input on STDIN (supports multiple lines too). e.g. perl -Xlpi50 crunch.pl <<< "Duis commodo scelerisque ex, ac consectetur metus rhoncus."

crunch.pl:

s/.*\K[aeiou]|.*\K |.$// while pos=-$^I
\$\endgroup\$
4
  • \$\begingroup\$ You don't need the space between /$+/ and while \$\endgroup\$
    – hmatt1
    Mar 1, 2016 at 16:00
  • \$\begingroup\$ I think you shave a byte by using a ^I (tab character) instead of "^I". (Untested.) \$\endgroup\$
    – msh210
    Mar 1, 2016 at 20:51
  • \$\begingroup\$ @chilemagic: dropping the space between /$+/ and while only works with older perls. Recent perls changed the parser to keep open the ability to add new regex modifiers (like a w modifier) \$\endgroup\$
    – Ton Hospel
    Mar 2, 2016 at 12:44
  • \$\begingroup\$ @msh210: Works for some magic variables, but not for the ones based on whitespace \$\endgroup\$
    – Ton Hospel
    Mar 2, 2016 at 12:45
6
\$\begingroup\$

MATL, 20 bytes

t11Y2mEG32=+K#Si:)S)

Try it online!

t       % implicitly input string. Duplicate
11Y2    % predefined literal 'aeiou'
m       % ismember. Gives true for input characters that are vowels
E       % multiply by 2
G       % push input string again
32      % ASCII for space
=       % gives true for input characters that are spaces
+       % add: gives 2 for vowels, 1 for space, 0 for non-vowels-and-non space
K#S     % sort and push only the indices of the sorting. Sorting is stable, so first 
        % will be non-vowels-and-non space characters in their original order, then
        % spaces in their original order, then vowels in their original order
i       % input number n of characters that should be kept
:       % range [1,2,...,n]
)       % index with that: keep first n indices of the sorting
S       % sort those indices to restore their original order
)       % index into input string to keep only those characters. Implicitly display
       
\$\endgroup\$
6
\$\begingroup\$

JavaScript (ES6), 66 61 bytes

Saved 5 bytes thanks to @Neil

f=(s,n)=>s[n]?f(s.replace(/(.*)[aeiou]|(.*) |.$/,"$1$2"),n):s

I don't think the regex is golfable further. Surprisingly, the shortest I can come up with to remove front-to-back is a byte longer:

f=(s,n)=>s[n]?f(s.replace(/(.*?)[aeiou]|(.*?) |./,"$1$2"),n):s

More interesting attempt (ES7), 134 bytes

(s,n,i=0)=>[for(c of s)[/[aeiou]/.test(c)*2+(c<'!'),i++,c]].sort(([x],[y])=>x-y).slice(0,n).sort(([,x],[,y])=>x-y).map(x=>x[2]).join``

This uses an approach similar to the MATL answer.

\$\endgroup\$
3
  • 1
    \$\begingroup\$ I don't care if it's not golfable, that's a beautiful regex. \$\endgroup\$
    – Neil
    Mar 1, 2016 at 11:49
  • \$\begingroup\$ Although I've just noticed that you can use |.$/,"$1$2" to save 5 bytes. \$\endgroup\$
    – Neil
    Mar 2, 2016 at 9:53
  • \$\begingroup\$ @Neil Thanks for the tip! \$\endgroup\$ Mar 2, 2016 at 15:01
2
\$\begingroup\$

sh + gnu sed, 78 61

Supply the string to STDIN, the length as first argument.

rev|sed -r ":                       # reverse + invoke sed + jump label ":"
/..{$1}/!q                          # if the length is not greater $1, quit
p                                   # print
s/[aeiou]//                         # delete the first vowel
t                                   # if successful, start over at ":"
s/ //                               # delete the first space
t                                   # if successful, start over at ":"
s/.//                               # delete the first character
t"|rev                              # if successful, start over at ":" + reverse
\$\endgroup\$
1
\$\begingroup\$

Perl, 68

Removing from the right adds a ton of characters, maybe there is a better way to do this.

$_=reverse;while(length>$^I){s/[aeiou]//||s/ //||s/.//}$_=reverse

Use -i to input the number. It is 65 characters plus 3 for the i, p, and l on the command line.

Run with:

echo 'Hello, Code Golf World!' | perl -i13 -ple'$_=reverse;while(length>$^I){s/[aeiou]//||s/ //||s/.//}$_=reverse'
\$\endgroup\$
1
  • \$\begingroup\$ You can use y///c instead of length and you can move the while loop to the end: s///||s///||s///while$^I<y///c \$\endgroup\$ Feb 29, 2016 at 23:38
1
\$\begingroup\$

Java 8, 303 bytes

(s,j)->{String t="";for(int i=s.length()-1;i>=0;t+=s.charAt(i--));while(t.length()>j&&t.matches(".*[aeiou].*"))t=t.replaceFirst("[aeiou]","");while(t.length()>j&&t.contains(" "))t=t.replaceFirst("\\s","");s="";for(int i=t.length()-1;i>=0;s+=t.charAt(i--));return s.substring(0,Math.min(t.length(),j));};

This is WAY too long. Ill try to shorten it soon. It would be much shorter if java had a method for reversing strings and backward replacements.

Test with the following:

public class StringCruncher {
    public static void main(String[] args) {
        Tester test = (s,j)->{String t="";for(int i=s.length()-1;i>=0;t+=s.charAt(i--));while(t.length()>j&&t.matches(".*[aeiou].*"))t=t.replaceFirst("[aeiou]","");while(t.length()>j&&t.contains(" "))t=t.replaceFirst("\\s","");s="";for(int i=t.length()-1;i>=0;s+=t.charAt(i--));return s.substring(0,Math.min(t.length(),j));};
        System.out.println(test.crunch("golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf", 150));
    }
}
interface Tester {
    String crunch(String s, int j);
}
\$\endgroup\$
4
  • 1
    \$\begingroup\$ The underwhelming majority on meta says you can save a byte with currying \$\endgroup\$
    – Cyoce
    Mar 1, 2016 at 0:32
  • \$\begingroup\$ @Cyoce it seems that currying does not work in this case (s->j->{...}). I think either Java does not support it very well or I am setting it up wrong. \$\endgroup\$
    – GamrCorps
    Mar 1, 2016 at 1:15
  • \$\begingroup\$ Compiled languages probably have a hard time with currying because of first class functions \$\endgroup\$ Mar 1, 2016 at 1:48
  • \$\begingroup\$ @GamrCorps I'll check and see if I can make it work when I get home \$\endgroup\$
    – Cyoce
    Mar 1, 2016 at 2:06
1
\$\begingroup\$

C#, 180 bytes

string c(int l,string s){while(s.Length>l){int i=0;Func<string,bool>f=t=>(i=s.LastIndexOfAny(t.ToCharArray()))!=-1;if(!(f("aeiou")||f(" ")))i=s.Length-1;s=s.Remove(i,1);}return s;}

Tester:

using System;
class Crunch
{
    static int Main()
    {
        var x = new Crunch();
        Console.WriteLine(x.c(50, "Duis commodo scelerisque ex, ac consectetur metus rhoncus."));
        Console.WriteLine(x.c(20, "Maecenas tincidunt dictum nunc id facilisis."));
        Console.WriteLine(x.c(150, "golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf golf"));
        Console.Read();
        return 0;
    }
    string c(int l,string s){while(s.Length>l){int i=0;Func<string,bool>f=t=>(i=s.LastIndexOfAny(t.ToCharArray()))!=-1;if(!(f("aeiou")||f(" ")))i=s.Length-1;s=s.Remove(i,1);}return s;}

    static string crunch(int len, string str)
    {
        Console.WriteLine($"{str.Length}: {str}");
        while (str.Length > len) {
            int idx=0;
            Func<string,bool> f = s => (idx = str.LastIndexOfAny(s.ToCharArray()))!= -1;
            if (!(f("aeiou") || f(" "))) idx = str.Length-1;
            str = str.Remove(idx,1);
            Console.WriteLine($"{str.Length}: {str}");
        }
        return str;
    }
}
\$\endgroup\$
1
\$\begingroup\$

Scala, 160 bytes

type S=String;def f(s:S,l:Int)={def r(s:S,p:S):S=if(s.size>l){val j=s.replaceFirst("(?s)(.*)"+p,"$1");if(j==s)s else r(j,p)}else s;r(r(r(s,"[aeiou]")," "),".")}

Tester:

val t="Hello, Code Golf World!"
println((t.size to 11 by -1).map(f(t,_)).mkString("\n"))
\$\endgroup\$
0
1
\$\begingroup\$

Dyalog APL, 77 45 42 bytes

t[⌽i~⌽⎕↓⌽∪∊(t∊'aeiou')(' '=t)1/¨⊂i←⍳⍴t←⌽⍞]

t[] letters of t with indices...
t←⌽⍞ t gets reversed text input
i←⍳⍴t i gets indices of length of t
/¨⊂i multiple (3) boolean selections of elements of i:
1. (t∊'aeiou') boolean where vowel
2. (' '=t) boolean where space
3. 1 all ∪∊ unique of the enlisted (flattened) 3 selections
⌽⎕↓⌽ drop last evaluated-inputted characters (same as (-⎕)↓)
⌽i~ reverse the remaining indices after removing some


Original answer:

⎕{⍺≥≢⍵:⌽⍵⋄∨/⍵∊⍨v←'aeiou':⍺∇⍵/⍨~<\(⍳⍴⍵)∊⍵⍳v⋄' '∊⍵:⍺∇⍵/⍨~<\(⍳⍴⍵)=⍵⍳' '⋄⍺∇1↓⍵}⌽⍞

Ehm, yeah, that is a bit hard to read. Basically the direct translation of OP into APL:

  1. Reverse input.
  2. If required length is longer or equal to the count of (reversed) input string, then return reversed (reversed) argument.
  3. Else, if argument has any vowel, remove the first (i.e. last) one and call recursively on what remains.
  4. Else, if argument has any space, remove the first (i.e. last) one and call recursively on what remains.
  5. Else, remove the first (i.e. last) character and call recursively on what remains.
\$\endgroup\$
1
\$\begingroup\$

Lua, 120 bytes

s=arg[2]:reverse()a=s:len()-arg[1]s,n=s:gsub('[aeiou]','',a)s,m=s:gsub(' ','',a-n)print(s:gsub('.','',a-n-m):reverse())

Takes input as command line arguments, in the format lua crunch.lua 10 "This is a string", with output Ths sstrng.

Explanation:

-- Set 's' to the reverse of the string
s=arg[2]:reverse()
-- Set 'a' to the number of characters to be removed
a=s:len()-arg[1]
-- Remove 'a' vowels, set 'b' to the number of substitutions
s,b=s:gsub('[aeiou]','',a)
-- Remove 'a-b' spaces, set 'c' to the number of substitutions
s,c=s:gsub(' ','',a-b)
-- Remove 'a-b-c' characters, and print the now un-reversed string
print(s:gsub('.','',a-b-c):reverse())
\$\endgroup\$
0
\$\begingroup\$

Mathematica, 201 Bytes

f@x_:=StringReplaceList[x,"a"|"e"|"i"|"o"|"u"->""];g@x_:=StringReplaceList[x," "->""];x_~l~y_:=NestWhile[If[f@#!={},Last@f@#,If[g@#!={},Last@g@#,Last@StringReplaceList[#,_->""]]]&,x,StringLength@#!=y&]

There must be a better way than this..

\$\endgroup\$
0
\$\begingroup\$

R, 169 143 bytes

function(x,y){d=utf8ToInt(x);o=c(rev(which(d%in%utf8ToInt('aeiou'))),rev(which(d==32)));intToUtf8(d[sort(tail(c(o,setdiff(nchar(x):1,o)),y))])}

*edit saved 36 bytes through rewrite with utf8ToInt -> intToUtf8 conversions not strstplit and paste0(...,collapse)

ungolfed with explanation

function(x,y){d=utf8ToInt(x);         # convert string (x) to integer
o=c(
 rev(which(d%in%utf8ToInt('aeiou'))), # index of vowels (reversed)
 rev(which(d==32)));                  # index of spaces
 intToUtf8(d[                         # convert subset back to character
   sort(tail(                         # return the first y index of 
                                      # "left over" characters
   c(o,setdiff(nchar(x):1,o))         # combine vowels, spaces and 
                                      # other indices in appropriate order
  ,y))])}
\$\endgroup\$

Your Answer

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

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