# The world ends in ed

Taken straight from the ACM Winter Programming Contest 2013. You are a person who likes to take things literally. Therefore, for you, the end of The World is ed; the last letters of "The" and "World" concatenated.

Make a program which takes a sentence, and output the last letter of each word in that sentence in as little space as possible (fewest bytes). Words are separated with anything but letters from the alphabet (65 - 90, 97 - 122 on the ASCII table.) That means underscores, tildes, graves, curly braces, etc. are separators. There can be more than one seperator between each word.

asdf jkl;__zxcv~< vbnm,.qwer| |uiop -> flvmrp
pigs, eat dogs; eat Bob: eat pigs -> ststbts
looc si siht ,gnitirw esreveR -> citwR
99_bottles_of_beer_on_the_wall -> sfrnel

• Could you add a test case including digits and underscores?
– grc
Mar 12 '13 at 4:56
• The world ends in ed? I knew vim and Emacs couldn't measure up! Mar 13 '13 at 3:29
• Well, the “real men use ed” essay has been part of the Emacs distribution for as long as I can remember.
– J B
Apr 11 '13 at 15:07
• Will the inputs be ASCII only? Mar 26 '18 at 14:21

# ed, 35 characters

s/[a-zA-Z]*$$[a-zA-Z]$$\|./\1/g
p
Q


So, the world ends in ed. As I like to be too literal, I decided to write to write the solution with ed - and apparently it is actually a programming language. It's surprisingly short, even considering many shorter solutions already exist in this thread. It would be nicer if I could use something other than [a-zA-Z], but considering ed isn't a programming language, it's actually good enough.

First, I would like to say this only parses the last line in file. It would be possible to parse more, just type , at beginning of two first lines (this specified "everything" range, as opposed to standard last line range), but that would increase code size to 37 characters.

Now for explanations. The first line does exactly what Perl solution does (except without support for Unicode characters). I haven't copied the Perl solution, I just invented something similar by coincidence.

The second line prints last line, so you could see the output. The third line forces quit - I have to do it, otherwise ed would print ? to remind you that you haven't saved the file.

Now for how to execute it. Well, it's very simple. Just run ed with the file containing test case, while piping my program, like that.

ed -s testcase < program


-s is silent. This prevents ed from outputing ugly file size at beginning. After all, I use it as a script, not editor, so I don't need metadata. If I wouldn't do that, ed would show file size that I couldn't prevent otherwise.

• I installed ed just to try this. Jun 14 '13 at 14:45
• 32 bytes... Try it Online! Nov 11 '20 at 22:50

## Perl 5, 18 bytes

s/\pL*(\pL)|./$1/g  Requires a -p command line switch. The named property L matches only letter characters A-Za-z. There are several hundred such named properties, but when dealing with ASCII text, very few of them are interesting. Besides \pL, the only other one of any real note is \pP, which matches punctuation. Try it online! Perl 5, 17 bytes A one byte improvement by Dom Hastings print/\pL*(\pL)/g  Requires -n (and -l to support multiple inputs). Try it online! Sample usage $ more in.dat
asdf jkl;__zxcv~< vbnm,.qwer| |uiop
pigs, eat dogs; eat Bob: eat pigs
looc si siht ,gnitirw esreveR
99_bottles_of_beer_on_the_wall

$perl -p ends-in-ed.pl < in.dat flvmrp ststbts citwR sfrnel  • I think \w also matches digits and underscores. – grc Mar 11 '13 at 5:20 • Hmm, indeed. That will need to be updated. Mar 11 '13 at 5:28 • Brilliant. Regex was an obvious solution, but |. was not obvious (to me, at least). Mar 11 '13 at 9:29 • Just noticed a -1 in print/\pL*(\pL)/g, seems to output the same for your test cases! Mar 16 '18 at 10:33 • s/(\pL)*|./$1/g and print/(\pL)*/g for 15 and 14 bytes since the capturing group captures here one char at a time so it store the last char of the word Jul 21 at 11:49

## Javascript, 49

alert(prompt().replace(/.(?=[a-z])|[^a-z]/gi,''))


It uses a regular expression to remove all characters that come before a letter, as well as all non-letter characters. Then we're left with the last letter of each word.

Thanks to tomsmeding for a nice improvement.

• You can probably improve this by making the regex case-insensitive, like in: alert(prompt().replace(/.(?=[a-z])|[^a-z]/gi,'')) Mar 11 '13 at 5:52

# C, 78

Golfed:

main(int c,char**s){for(;c=*s[1]++;)isalpha(c)&&!isalpha(*s[1])?putchar(c):0;}


With whitespace:

main(int c,char**s)
{
for(;c=*s[1]++;)
isalpha(c)&&!isalpha(*s[1])?putchar(c):0;
}


Output:

• You can save 4 bytes by using K&R declaration and defaulting c: main(c,s)char**s;{for Mar 21 '18 at 16:10

## GNU Sed, 4038 37

s/[a-z]\b/&\n/g; s/[^\n]*$$.$$\n/\1/g


### Testing

cat << EOF > data.txt
asdf jkl;__zxcv~< vbnm,.qwer| |uiop
pigs, eat dogs; eat Bob: eat pigs
looc si siht ,gnitirw esreveR
EOF


Run sed:

sed 's/[A-Za-z]\b/&\n/gi; s/[^\n]*$$.$$\n/\1/g' data.txt


Output:

flvmrp
ststbts
citwR


### Explanation

The first substitution replaces all word boundaries, that are preceded by the desired match group, with a new-line. This makes it easy to remove all extraneous characters in the second substitution.

### Edit

• Use case-insensitive flag (-2), thanks manatwork.
• Don't count whitespace (-1).
• sed's s command has i flag for cases insensitive matching: s/[a-z]\b/&\n/gi. Mar 11 '13 at 9:29
• @manatwork: good point, this would make it GNU sed only, but it seems it already is, thanks.
– Thor
Mar 11 '13 at 9:38
• \b considers _s to be letters, so if any words in the test END with _, that word's last letter is not included in the output Apr 16 '14 at 17:41

## Grep and Paste, 3634 28

> echo 'asdf jkl;__zxcv~< vbnm,.qwer| |uiop' | grep -io '[a-z]\b' | tr -d \\n
flvmrp

> echo 'pigs, eat dogs; eat Bob: eat pigs'   | grep -io '[a-z]\b' | tr -d \\n
ststbts

echo 'looc si siht ,gnitirw esreveR'         | grep -io '[a-z]\b' | tr -d \\n
citwR


If a final new-line is needed, replace tr -d \\n with paste -sd ''.

### Edit

• Use case-insensitive grep (-2), thanks manatwork.
• Use tr instead of paste (-4), thanks manatwork.
• Don't count whitespace around pipe (-2).
• Quite creative with that paste -sd '', but tr -d \\n is shorter. Regarding grep, it has -i switch meaning “ignore case”, which can make it shorter: grep -io '[a-z]\b'. Mar 11 '13 at 9:24
• @manatwork, tr also delete the final newline. Case insensitive mode is of course shorter, thanks.
– Thor
Mar 11 '13 at 9:29
• there is no rule requiring final newline. Mar 11 '13 at 9:32
• @manatwork: I can agree with that, updated answer.
– Thor
Mar 11 '13 at 9:44

## sed, 37 chars

Equal length to Thor's answer, but, I think, simpler.

s/[a-z]*$$[a-z]$$/\1/ig;s/[^a-z]*//ig


The logic is quite trivial - replace letter sequences with their last letter, then delete all non-letters.

## Mathematica, 39

""<>StringCases[#,(__~~x_)?LetterQ:>x]&


Test:

""<>StringCases[#,(__~~x_)?LetterQ:>x]& /@
{"asdf jkl;__zxcv~< vbnm,.qwer| |uiop",
"pigs, eat dogs; eat Bob: eat pigs",
"looc si siht ,gnitirw esreveR",
"99_bottles_of_beer_on_the_wall"}

{"flvmrp", "ststbts", "citwR", "sfrnel"}

• Good one. LetterQ should be called LettersQ :) I haven't thought of it for testing whole strings. Mar 27 '13 at 11:47
• @belisarius Actually, with this construct it is applied character-wise, so it could be a literal "LetterQ" and still work. Mar 27 '13 at 20:08

# K, 49

{last'f@&"b"$#:'f:"|"\:@[x;&~x in,/.QaA;:;"|"]}  . k){last'f@&"b"$#:'f:"|"\:@[x;&~x in,/.QaA;:;"|"]}"asdf jkl;__zxcv~< vbnm,.qwer| |uiop"
"flvmrp"
k){last'f@&"b"$#:'f:"|"\:@[x;&~x in,/.QaA;:;"|"]}"pigs, eat dogs; eat Bob: eat pigs" "ststbts" k){last'f@&"b"$#:'f:"|"\:@[x;&~x in,/.QaA;:;"|"]}"looc si siht ,gnitirw esreveR"
"citwR"


## Scala, 59 (or 43)

Assuming the string in already in s:

s.split("[^a-zA-Z]+").map(_.last).mkString


If you need to read from a prompt and print rather than using the REPL output, convert s to readLine and wrap in println() for 59.

## x86: 54 bytes

Assume a cdecl routine with the signature void world_end(char *input, char *output):

60 8b 74 24 24 8b 7c 24 28 33 d2 8a 0e 8a c1 24
df 3c 41 72 08 3c 5a 77 04 8a d1 eb 09 84 d2 74
05 88 17 47 33 d2 46 84 c9 75 e0 84 d2 74 03 88
17 47 88 0f 61 c3

• By the way, I realize the question asks for a program and not a routine, but I wanted to do something different. Contrary to the problem statement, I guess I'm not a "person who likes to take things literally" after all. :P Mar 11 '13 at 18:18

println$@{=>.-1}<>input re"\W+"  Xi is a language still in its beta phase, but it seems to work well with code golf so I figured I might as well show yet another short and functional solution (and advertise the language a little :-)). ## Mathematica 6257 52 Row@StringTake[StringCases[#,LetterCharacter..],-1]&  Testing l = {"asdf jkl;__zxcv~<vbnm,.qwer| |uiop", "pigs,eat dogs;eat Bob:eat pigs", "looc si siht,gnitirw esreveR"} Row@StringTake[StringCases[#,LetterCharacter..],-1]&/@ l (*{flvmrp,ststbts,citwR}*)  • I mistakenly edited yours, but then rolled it back. Ooops. Mar 13 '13 at 21:01 # Python3, 59 chars import re;print(re.sub('.(?=[a-z])|[^a-z]','',input(),0,2))  Correctly deals with capital letters and underscores. The 2 is to pass re.sub the re.IGNORECASE flag without having to use re.I. # Python, 76 chars import re;print "".join(re.findall("([a-zA-Z])(?=$|[^a-zA-Z])",raw_input()))

• You can remove the space after print. Mar 29 '13 at 0:28
• Shorten by porting to Python 3: import re;print(*re.findall("([a-zA-Z])(?=$|[^a-zA-Z])",input()),sep='') Mar 29 '13 at 20:38 # R, 126 My R solution function(x){cat(paste(unlist(sapply(strsplit(unlist(strsplit(gsub(" [^[:alpha:]]"," ",x)," ")),""),tail,1)),collapse=""),"\n")}  • Nice approach, golfed down a bit to 111 bytes... Nov 11 '20 at 22:06 # Python 3.x, 64 bytes import re;print(''.join(a[-1] for a in re.split('\W+',input())))  • The last example is not working. Also, an error occurs if the line begins or ends with a separator – AMK Mar 12 '13 at 13:32 • You can remove the space before for. Apr 2 '13 at 10:55 # Lua, 42 print(((...):gsub('.-(.)%f[%A]%A*','%1')))  Usage example: lua script.lua "asdf jkl;__zxcv~< vbnm,.qwer| |uiop" ## Mathematica 714745 61 Back to the drawing board, after @belisarius found an error in the code. StringCases[#, RegularExpression["[A-Za-z](?![A-Za-z])"]] <> "" &  Testing l = {"asdf jkl;__zxcv~<vbnm,.qwer| |uiop", "asdf jkl__zxcv~<vbnm,.qwer| |uiop", "pigs,eat dogs;eat Bob:eat pigs", "looc si siht,gnitirw esreveR"}; StringCases[#, RegularExpression["[A-Za-z](?![A-Za-z])"]] <> "" & /@ l  {"flvmrp", "flvmrp", "ststbts", "citwR"} • \\w matches _, so it doesn't work for (for example) "asdf jkl__zxcv~<vbnm,.qwer| |uiop" Mar 13 '13 at 12:23 • Wait Row@StringTake[ StringCases[#, LetterCharacter ..], -1] &@"asdf jkl__zxcv~<vbnm,.qwer| |uiop" gives me flvmrp, but #~StringCases~RegularExpression@"\\w\\b" <> "" &@"asdf jkl__zxcv~<vbnm,.qwer| |uiop" returns fvmrp here. Are we getting the same results?? Mar 13 '13 at 13:39 • @belisarius You were right about the error in my earlier version. I was testing it with the wrong string! Mar 14 '13 at 1:34 • Hehe , +1 again Mar 14 '13 at 2:59 • @belisarius guys, please see the answer I posted. If it is correct it's shorter. Mar 27 '13 at 6:34 # Python 2, 88 80 75 69 68 s=p='' for c in raw_input()+' ':a=c.isalpha();s+=p[a:];p=c*a print s  Input: 435_ASDC__uio;|d re;fG o55677jkl..f Output: CodeGolf This solution can be shortened to 67 characters if you allow the output to include backspace characters (ASCII code 8) at the beginning. The output will be visually identical. s=p='<BS>' for c in raw_input()+p:a=c.isalpha();s+=p[a:];p=c*a print s  Same input, (visually) same output. <BS> is meant to be the backspace character. # C# Method, 105 bytes: (assumes usings for System, System.Text.RegularExpressions and System.Linq) string R(string i){return string.Concat(Regex.Split(i,"[^a-zA-Z]").Where(x=>x!="").Select(n=>n.Last()));}  Program, 211 bytes: using System;using System.Text.RegularExpressions;using System.Linq;class A{static void Main(){Console.WriteLine(string.Concat(Regex.Split(Console.ReadLine(),"[^a-zA-Z]").Where(x=>x!="").Select(n=>n.Last())));}}  ## VBA, 147 161 Sub a(s) For n=0 To 255:m=Chr(n):s=Replace(s,IIf(m Like"[A-Za-z]","",m)," "):Next For Each r In Split(s," "):t=t & Right(r,1):Next MsgBox t End Sub  # Ruby 2.0, 25 (+1) chars gsub(/(\w+)\W*/){$1[-1]}


Must be run with the -p switch:

 $ruby -p ed.rb <<< "asdf jkl;__zxcv~< vbnm,.qwer| |uiop" flvmrp  • Please specify the ruby version. 1.9.2 outputs “#<Enumerator:0x9f65e10>#<Enumerator:0x9f65d98>#<Enumerator:0x9f65d34>#<Enumerator:0x9f65cd0>”. Jun 15 '13 at 11:38 • You're right. I had completely forgotten that I installed it, but my current Ruby version is 2.0 (ruby 2.0.0p0 (2013-02-24 revision 39474). When running the program with version 1.8.7 it outputs an ASCII value! Didn't know there were so many differences between the versions. Jun 15 '13 at 16:23 • Thanks, will definitely have to update my Ruby. (Both the interpreter and the knowledge.) The second capturing group is not necessary: gsub(/(\w+)\W*/){$1[-1]}. Jun 15 '13 at 17:06
• Oh, of course it's not. Thanks, updated :) Jun 15 '13 at 18:33

# Vim, 26 bytes:

:s/\v\S*(\a)\A*( |$)/\1/g<CR>  Try it online! # Retina, 16 bytes Li, -1|""[a-z]+  Try it online! ### Explanation Li, -1|""[a-z]+ L [a-z]+ List all the sequences of letters in the input i case insensitive , Keep all the results -1 but only the last character for each of them |"" Use the empty string as separator  # Retina, 20 bytes i.(?=[a-z])|[^a-z]  Try it online This program is compatible with version 0.8.2 # Java 8, 43 bytes s->s.replaceAll("(?i).(?=[a-z])|[^a-z]","")  Port of @mbomb007's Retina answer. Explanation: Try it online. s-> // Method with String as both parameter and return-type s.replaceAll("(?i).(?=[a-z])|[^a-z]","") // Remove every match of this regex, and return as result  Additional explanation for the regex: "(?i).(?=[a-z])|[^a-z]" // Main regex to match (?i) // Case insensitive . // Any character (?=[a-z]) // Followed by a letter (as positive look-ahead) |[^a-z] // or a non-letter "" // Replace it with: nothing  • That's actually (?i) for the flag. Mar 24 '18 at 20:53 # GAWK, 49 46 bytes BEGIN{RS="[^A-z]"}{printf substr($0,length\$0)}


Try it online!

# Husk, 10 bytes

m→fo√←ġo¬√


Try it online!

since there's no conditional spliting function, group-by is used. Golflangs with regex are really strong here.

## Explanation

m→fo√←ġo¬√
ġo¬√ group on nonalphabets
fo√←     filter out nonalphabets
m→         map to last character

• Well done!!! Nonalphabets is the secret! I was trying to do this last night, and ended up with this, which is completely embarassing and so I was ashamed to post it! Nov 12 '20 at 8:10
• One of my first husk answers had this same problem and √ was very helpful. Nov 12 '20 at 8:12

# Vyxals, 6 bytes

Thanks to lyxal for reminding me not to be a sussy baka

øWǍƛ[t


Try it Online!

Explanation:

øW      # Group words together
Ǎ     # Remove non-alphabet characters
ƛ    # For each remaining string:
[   #   If it is not empty:
t  #     Tail
`