30
\$\begingroup\$

Given a string, find the first word starting with each letter (case insensitive).

Sample

Using Ferulas flourish in gorgeous gardens. as input:

"Ferulas flourish in gorgeous gardens."
 ^^^^^^^ ^^ ^^^^^^^^
 | | |
 | | --> is the first word starting with `g`
 | --> is the first word starting with `i`
 --> is the first word starting with `f`

Then, the output for this sample should be the matched words joined by one single space:

"Ferulas in gorgeous"

Challenge

Both input and output must be a string representation, or the closest alternative in your language.

Program or function allowed.

You can consider a word being at least one of: lowercase or uppercase letters, digits, underscore.

This is , shortest answer in bytes wins.

Another samples:

input: "Take all first words for each letter... this is a test"
output: "Take all first words each letter is"

input: "Look ^_^ .... There are 3 little dogs :)"
output: "Look _ There are 3 dogs"

input: "...maybe some day 1 plus 2 plus 20 could result in 3"
output: "maybe some day 1 plus 2 could result in 3"
asked Apr 7, 2016 at 3:33
\$\endgroup\$
2
  • \$\begingroup\$ Are trailing/starting spaces allowed? <s>Can I assume words are separated by one space in original string?</s> \$\endgroup\$ Commented Apr 8, 2016 at 10:10
  • \$\begingroup\$ Iunderstood it from the examples, so there is <s></s> in the comment. What about trimming spaces? \$\endgroup\$ Commented Apr 8, 2016 at 10:47

34 Answers 34

1
2
17
\$\begingroup\$

Retina, 28 bytes:

M!i`\b(\w)(?<!\b1円.+)\w*
¶
 
  • M! - Match each work and print all words separated by newlines.
  • i - Ignore case.
  • \b(\w) - Capture first letter of each word
  • (?<!\b1円.+) - After matching the letter, check if there wasn't a previous word starting with the same letter. 1円.+ ensures at least two characters, so we are skipping the current word.
  • \w* - match the rest of the word.
    The above matches only words - all other characters are removed.
  • ¶\n - Replace newlines with spaces.

Try it online!

answered Apr 7, 2016 at 6:20
\$\endgroup\$
10
\$\begingroup\$

JavaScript (ES6), (削除) 73 (削除ここまで) 71 bytes

s=>s.match(u=/\w+/g).filter(w=>u[n=parseInt(w[0],36)]?0:u[n]=1).join` `

Saved 2 bytes thanks to @edc65!

Test

var solution = s=>s.match(u=/\w+/g).filter(w=>u[n=parseInt(w[0],36)]?0:u[n]=1).join` `;
var testCases = [
 "Ferulas flourish in gorgeous gardens.",
 "Take all first words for each letter... this is a test",
 "Look ^_^ .... There are 3 little dogs :)",
 "...maybe some day 1 plus 2 plus 20 could result in 3"
];
document.write("<pre>"+testCases.map(t=>t+"\n"+solution(t)).join("\n\n")+"</pre>");

answered Apr 7, 2016 at 4:54
\$\endgroup\$
4
  • \$\begingroup\$ Using parseInt("_",36) = NaN? Blasphemy! \$\endgroup\$ Commented Apr 7, 2016 at 5:00
  • 1
    \$\begingroup\$ The fun fact is: it works @Sp3000 \$\endgroup\$ Commented Apr 7, 2016 at 6:57
  • \$\begingroup\$ Using u=regexp is really clever. Save 2 bytes s=>s.match(u=/\w+/g).filter(w=>u[w=parseInt(w[0],36)]?0:u[w]=1).join' ' \$\endgroup\$ Commented Apr 7, 2016 at 7:00
  • \$\begingroup\$ @edc65 Thanks. It's actually quite convenient that there are 37 possible outputs for a single base-36 digit. \$\endgroup\$ Commented Apr 7, 2016 at 7:04
9
\$\begingroup\$

Retina, 45 bytes

i`\b((\w)\w*)\b(?<=\b2円\w*\b.+)
\W+
 
^ | $

Simply uses a single regex to remove later words starting with the same \w character (case insensitive with the i option), converts runs of \W to a single space, then removes any leading/trailing space from the result.

Try it online!

Edit: See @Kobi's answer for a shorter version using M!`

answered Apr 7, 2016 at 3:57
\$\endgroup\$
5
  • \$\begingroup\$ Darn it, barely beat me to it! I couldn't figure out the lookbehind though. \$\endgroup\$ Commented Apr 7, 2016 at 3:58
  • 3
    \$\begingroup\$ I've added another Retina answer - I think that's OK if they are different enough (the basic concept is similar, of course). \$\endgroup\$ Commented Apr 7, 2016 at 6:23
  • 1
    \$\begingroup\$ @Kobi It's much better, so I'm glad to see it :) Makes me realise how much more I need to learn about Retina's line options and what not. \$\endgroup\$ Commented Apr 7, 2016 at 6:26
  • \$\begingroup\$ Could you do this to save a few bytes? i` \b((\w)\w*)\b(?<=\b2円\w*\b.+) (a space before the first \b) Are the lines afterwards unnecessary? \$\endgroup\$ Commented Apr 8, 2016 at 1:19
  • \$\begingroup\$ @KennyLau Unfortunately, I don't think that works because words aren't necessarily separated by spaces, e.g. a...a -> a \$\endgroup\$ Commented Apr 8, 2016 at 2:36
7
\$\begingroup\$

Pyth, 23 bytes

J:z"\w+"1jdxDJhM.grhk0J

Try it online: Demonstration or Test Suite

J:z"\w+"1 finds all the words in the input using the regex \w+ and stores them in J.

.grhk0J groups the words by their lowercase first letter, hM takes the first from each group, xDJ sorts these words by their index in the input string, and jd puts spaces between them.

answered Apr 7, 2016 at 7:14
\$\endgroup\$
4
\$\begingroup\$

Perl 6, 39 bytes

{.words.grep({!%.{.substr(0,1).lc}++})}
answered Apr 7, 2016 at 8:49
\$\endgroup\$
1
  • 1
    \$\begingroup\$ 42 bytes which fixes the words having to match \w+ and golfs the substr part \$\endgroup\$ Commented Dec 29, 2018 at 12:26
3
\$\begingroup\$

C, (削除) 142 (削除ここまで) (削除) 132 (削除ここまで) 122 bytes

10 bytes lighter thanks to @tucuxi!

b[200],k;main(c){for(;~c;isalnum(c)|c==95?k&2?:(k|=!b[c|32]++?k&1?putchar(32):0,7:2),k&4?putchar(c):0:(k&=1))c=getchar();}

(削除) Prints a trailing space after the last output word. (削除ここまで)

answered Apr 7, 2016 at 9:08
\$\endgroup\$
1
  • 1
    \$\begingroup\$ you can shave the checks for c>47 and c<58 by using isalnum instead of isalpha \$\endgroup\$ Commented Apr 7, 2016 at 13:43
3
\$\begingroup\$

MATL, 23 bytes

'\w+'XXtck1Z)t!=XRa~)Zc

This borrows Jakube's idea of using a regexp for removing unwanted characters and splitting at the same time.

Input is a string enclosed by single quotes.

Try it online!

Explanation

'\w+'XX % find words that match this regexp. Gives a cell array
t % duplicate
c % convert into 2D char array, right-padded with spaces
k % make lowercase
1Z) % get first column (starting letter of each word)
t!= % duplicate, transpose, test for equality: all combinations 
XR % set diagonal and below to 0
a~ % true for columns that contain all zeros 
) % use as a logical index (filter) of words to keep from the original cell array
Zc % join those words by spaces
answered Apr 7, 2016 at 8:34
\$\endgroup\$
2
\$\begingroup\$

Vim 57 keystrokes

:s/[^a-zA-Z_ ]//g<cr>A <cr>ylwv$:s/\%V\c<c-v><c-r>"\h* //eg<c-v><cr>@q<esc>0"qDk@q

Explanation:

:s/[^a-zA-Z_ ]//g #Remove all invalid chars.
A <cr> #Enter insert mode, and enter 
 #a space and a newline at the end
ylwv$:s/\\c%V<c-v><c-r>"\h* //eg<c-v><cr>@q<esc> #Enter all of this text on the 
 #next line
0 #Go to the beginning of the line
"qD #Delete this line into register
 #"q"
k@q #Run "q" as a macro 
#Macro
ylw #Yank a single letter
 v$ #Visual selection to end of line
 :s/ #Substitute regex
 \%V\c #Only apply to the selection and 
 #ignore case
 <c-v><c-r>" #Enter the yanked letter
 \h* #All "Head of word" chars
 #And a space
 // #Replace with an empty string
 eg #Continue the macro if not found
 #Apply to all matches
 <c-v><cr> #Enter a <CR> literal
 @q<esc> #Recursively call the macro

I'm really dissapointed by how long this one is. The "Invalid" chars (everything but a-z, A-Z, _ and space) really threw me off. I'm sure there's a better way to do this:

:s/[^a-zA-Z_ ]//g

Since \h matches all of that expect for the space, but I can't figure out how to put the metachar in a range. If anyone has tips, I'd love to hear em.

answered Apr 7, 2016 at 4:13
\$\endgroup\$
1
  • 3
    \$\begingroup\$ why a-zA-Z_ and not \w? digits are valid \$\endgroup\$ Commented Apr 7, 2016 at 7:18
2
\$\begingroup\$

Julia, (削除) 165 (削除ここまで) (削除) 155 (削除ここまで) (削除) 151 (削除ここまで) (削除) 129 (削除ここまで) 102 bytes

g(s,d=[])=join(filter(i->i!=0,[(c=lcfirst(w)[1])∈d?0:(d=[d;c];w)for w=split(s,r"\W",keep=1<0)])," ")

This is a function that accepts a string and returns a string.

Ungolfed:

function g(s, d=[])
 # Split the string into an array on unwanted characters, then for
 # each word, if the first letter has been encountered, populate
 # this element of the array with 0, otherwise note the first letter
 # and use the word. This results in an array of words and zeros.
 x = [(c = lcfirst(w)[1]) ∈ d ? 0 : (d = [d; c]; w) for w = split(s, r"\W", keep=1<0)]
 # Remove the zeros, keeping only the words. Note that this works
 # even if the word is the string "0" since 0 != "0".
 z = filter(i -> i != 0, x)
 # Join into a string and return
 return join(z, " ")
end

Saved 53 bytes with help from Sp3000!

answered Apr 7, 2016 at 5:00
\$\endgroup\$
2
\$\begingroup\$

Jelly, (削除) 32 (削除ここまで) 31 bytes

ØB;"_
e€¢¬œṗf€1ドルÐfμZḢŒlQi@€$ịj6

Try it online!

answered Apr 7, 2016 at 6:12
\$\endgroup\$
2
\$\begingroup\$

C# (LINQPAD) - (削除) 136 (削除ここまで) 128 bytes

var w=Util.ReadLine().Split(' ');string.Join(" ",w.Select(s=>w.First(f=>Regex.IsMatch(""+f[0],"(?i)"+s[0]))).Distinct()).Dump();
answered Apr 7, 2016 at 7:20
\$\endgroup\$
2
\$\begingroup\$

05AB1E, 40 bytes

Code:

94L32+çJžj-DU-ð¡""Kvy¬Xsl©åï>iX®«Uy}\}ðý

Try it online!

Explanation:

We first generate all characters which should be deleted from the input string using 94L32+ç (Try here). We join this string using J and remove [a-zA-Z0-9_] which is stored in žj (Try here). We remove all the characters that are in the second string from the first string, which will leave us:

!"#$%&'()*+,-./:;<=>?@[\]^`{|}~

That can also be tested here. We Duplicate this and store in to X with the U-command. We then remove all the characters that are in this string from the input. We then split on whitespaces using ð¡ and remove all empty strings (using ""K). We now have this.

This is the clean version of the input, which we will work with. We map over each element using v. This uses y as the string variable. We take the first character of the string using ¬ and push X, which contains a string with all forbidden characters (!"#$%&'()*+,-./:;<=>?@[\]^`{|}~). We check if the lowercase version of the first character, (which will also be ©opied to the register), is in this string using å. Covered by this part: ï>i, if the first letter doesn't exist in the string of forbidden characters (X), we append this letter to the list of forbidden characters (done with X®«U) and we push y on top of the stack.

Finally, when the strings are filtered, we join the stack by spaces with ðý.

answered Apr 7, 2016 at 5:50
\$\endgroup\$
2
  • 1
    \$\begingroup\$ ... explanation? :-) \$\endgroup\$ Commented Apr 8, 2016 at 9:59
  • \$\begingroup\$ @LuisMendo Thanks for reminding me! Done :) \$\endgroup\$ Commented Apr 8, 2016 at 11:24
2
\$\begingroup\$

PHP

Inspired by the use of regex in most of the answers, I originally tried to do this without using regex at all just to show off a neat variation, but the sticking point of not having clean strings as input ruined that idea. Sad.

With function wrapper, 89 bytes

function f($s){foreach(preg_split('/\W/',$s)as$w)$c[lcfirst($w)[0]]++?:$v.=" $w";echo$v;}

Without function wrapper (needing $s pre-declared), 73 bytes

foreach(preg_split('/\W/',$s)as$w)$c[lcfirst($w)[0]]++?:$v.=" $w";echo$v;

Explanation:

foreach(preg_split('/\W/',$s)as$w)$c[lcfirst($w)[0]]++?:$v.=" $w";echo$v;
 preg_split('/\w/',$s) Break input on all non-word characters
foreach( as$w) Loop through each 'word'
 lcfirst($w)[0] Take the first letter of the lowercase version of the word
 $c[ ]++?: Increment an array element with a key of that letter after checking if it's false-y (0)
 $v.=" $w"; Add the word if the letter wasn't found (if the previous condition evaluated to false)
 echo$v; Print the new string to screen.

My only regret is that I couldn't find a faster way of checking/converting letter case.

answered Apr 8, 2016 at 14:47
\$\endgroup\$
2
\$\begingroup\$

Python, 103 bytes

import re
lambda s,d=[]:[w for w in re.findall("\w+",s)if(d.append(w.lower()[0])or d[-1])not in d[:-1]]
answered Apr 8, 2016 at 21:41
\$\endgroup\$
2
\$\begingroup\$

grep and awk, (削除) 68 (削除ここまで) 56 bytes

The script:

echo `grep -o '\w*'|awk '!x[tolower(substr(0,1,1ドル))]++'`

Explanation:

  • grep -o matches the legal words, printing each on its own line.

  • awk takes the first letter of each line with substr, makes it lowercase, and then increments a hashtable entry with that key. If the value was unset before the increment, the line is printed.

  • echo ... turns the lines back into words

I previously tried to create a solution without awk, using uniq, sort, grep and bash but fell just short. History in the edits.

Thanks to Dennis for some improvements I missed.

answered Apr 10, 2016 at 3:05
\$\endgroup\$
0
2
\$\begingroup\$

Ruby, 40 bytes

->s{s.scan(/\w+/).uniq{|x|x.ord|32}*' '}

Try it online!

String#ord returns the codepoint of the first character in a string and bitwise or with 32 maps uppercase codepoints to the lowercase ones and 95 (_) to 127, while keeping everything else the same.

answered Jul 14, 2021 at 7:50
\$\endgroup\$
1
\$\begingroup\$

Lua, 172 Bytes

It ended up way longer that I wanted...

t={}(...):gsub("[%w_]+",function(w)b=nil for i=1,#t
do b=t[i]:sub(1,1):lower()==w:sub(1,1):lower()and 1 or b
end t[#t+1]=not b and w or nil end)print(table.concat(t," "))

Ungolfed

t={} -- initialise the accepted words list
(...):gsub("[%w_]+",function(w)-- iterate over each group of alphanumericals and underscores
 b=nil -- initialise b (boolean->do we have this letter or not)
 for i=1,#t -- iterate over t
 do
 b=t[i]:sub(1,1):lower() -- compare the first char of t's i word
 ==w:sub(1,1):lower() -- and the first char of the current word
 and 1 -- if they are equals, set b to 1
 or b -- else, don't change it
 end
 t[#t+1]=not b and w or nil -- insert w into t if b isn't set
end)
print(table.concat(t," ")) -- print the content of t separated by spaces
answered Apr 7, 2016 at 7:15
\$\endgroup\$
1
\$\begingroup\$

Seriously, 43 bytes

6╙¬▀'_+,;)-@s`;0@Eùk`M┬i;╗;lrZ`i@╜í=`M@░' j

Try it online!

The lack of regex capabilities made this much more difficult than it needed to be.

Explanation:

6╙¬▀'_+,;)-@s`;0@Eùk`M┬i;╗;lrZ`i@╜í=`M@░' j
6╙¬▀ push digits in base 62 (uppercase and lowercase letters and numbers)
 '_+ prepend underscore
 ,;) push two copies of input, move one to bottom of stack
 - get all characters in input that are not letters, numbers, or underscores
 @s split input on all occurrences of non-word characters
 `;0@Eùk`M for each word: push the first letter (lowercased)
 ┬i transpose and flatten (TOS is list of first letters, then list of words)
 ;╗ push a copy of the first letters list to register 0
 ;lrZ zip the list of first letters with their positions in the list
 `i@╜í=`M for each first letter: push 1 if that is the first time the letter has been encountered (first index of the letter matches its own index) else 0
 @░ filter words (take words where corresponding element in the previous list is truthy)
 ' j join on spaces
answered Apr 8, 2016 at 2:18
\$\endgroup\$
1
\$\begingroup\$

Ruby 76 Bytes

s;f={};s.scan(/(([\w])[\w]*)/).map{|h,i|f[j=i.upcase]?nil:(f[j]=!p; h)}.compact.*' '

Or with method definition 88 bytes

def m s;f={};(s.scan(/((\w)\w*)/).map{|h,i|f[j=i.upcase]?nil:(f[j]=1; h)}-[p]).*' ';end

Ungolfed and with unit test:

def m_long(s)
 #found - Hash with already found initials
 f={}
 #h=hit, i=initial, j=i[0].downcase
 s.scan(/(([\w\d])[\w\d]*)/).map{|h,i| 
 f[j=i.upcase] ? nil : (f[j] = true; h)
 }.compact.join(' ')
end
#true == !p
#~ def m(s)
 #~ f={};s.scan(/(([\w\d])[\w\d]*)/).map{|h,i|f[j=i.upcase]?nil:(f[j]=!p; h)}.compact.join' '
#~ end
def m s;f={};s.scan(/(([\w\d])[\w\d]*)/).map{|h,i|f[j=i.upcase]?nil:(f[j]=!p; h)}.compact.join' ';end
#~ s = "Ferulas flourish in gorgeous gardens."
#~ p s.split
require 'minitest/autorun'
class FirstLetterTest < Minitest::Test
 def test_1
 assert_equal("Ferulas in gorgeous",m("Ferulas flourish in gorgeous gardens."))
 assert_equal("Ferulas in gorgeous",m_long("Ferulas flourish in gorgeous gardens."))
 end
 def test_2
 assert_equal("Take all first words each letter is",m("Take all first words for each letter... this is a test"))
 assert_equal("Take all first words each letter is",m_long("Take all first words for each letter... this is a test"))
 end
 def test_3
 assert_equal("Look _ There are 3 dogs",m("Look ^_^ .... There are 3 little dogs :)"))
 assert_equal("Look _ There are 3 dogs",m_long("Look ^_^ .... There are 3 little dogs :)"))
 end
 def test_4
 assert_equal("maybe some day 1 plus 2 could result in 3",m("...maybe some day 1 plus 2 plus 20 could result in 3"))
 assert_equal("maybe some day 1 plus 2 could result in 3",m_long("...maybe some day 1 plus 2 plus 20 could result in 3"))
 end
end
answered Apr 7, 2016 at 19:20
\$\endgroup\$
5
  • \$\begingroup\$ In Regex, \w includes number characters, so [\w\d] can be replaced with \w. Also, if nil values are in an array when you call join' ' (or better yet, *' ' is a shorthand you can use to save more bytes), they vanish, so the call to compact is unnecessary. \$\endgroup\$ Commented Apr 8, 2016 at 6:27
  • \$\begingroup\$ @KevinLau Thanks. The \w\dis embarrassing for me. But if I remove the compact I get additional spaces, (see ['x',nil,'x']*'y' == 'xyyx'). Or did I miss something? \$\endgroup\$ Commented Apr 8, 2016 at 8:56
  • \$\begingroup\$ Whoops, you're right. In that case, (list-[p]) saves bytes over list.compact. Also, /\w/ is equivalent to /[\w]/. Finally, you can replace your nil with p and your !p with 1 (since your hash only needs truthy values in it) \$\endgroup\$ Commented Apr 8, 2016 at 9:08
  • \$\begingroup\$ Thanks, I added your remarks, The replacement of nil with p does not work. If I use it inside my code I get a syntax error. I have to encapsulate like (p) - but then I have again 3 characters. \$\endgroup\$ Commented Apr 8, 2016 at 9:56
  • \$\begingroup\$ Flip the ternary and then it works to save a byte: !f[j=i.upcase]?(f[j]=1;h):p. Also just thought of this, but because of string indexing, using s.scan(/\w+/) and removing the i in favor of h[0] works too. \$\endgroup\$ Commented Apr 8, 2016 at 21:13
1
\$\begingroup\$

Husk, 22 bytes

wÖ11ドルm←ko_←1
mf§|='_しろいしかくw

Try it online!

The '_' requirement is a bit annoying, otherwise a pretty interesting challenge.

Explanation

function 1: filter nonalphanumerics and split
mf§|='_しろいしかくw 
 w split on spaces
mf map and filter each letter in the words by:
 しろいしかく is it alphanumeric?
 §| or:
 ='_ is it an underscore?
main function:
wÖ11ドルm←ko_←1
 1 format the input
 ko key the words on:
 _← first letter, lowercased
 m← map each to first word
 Ö order by:
 11ドル their index in the formatted input
w join back with spaces
answered Nov 14, 2020 at 3:10
\$\endgroup\$
1
\$\begingroup\$

K (ngn/k), 55 bytes

{" "/w@.*'" "_=_*'w:" "\c(c:`c$&2!&48,273988544219円)?x}

Try it online!

  • (c:`c$&2!&48,273988544219円) a compressed version of "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz", stored in c
  • c(...)?x replace all non-alphanumeric/underscore characters with spaces
  • w:" "\ split the converted input on spaces, storing in w
  • =_*'w build a dictionary mapping the distinct (lowercased) leading characters to their words' position(s) within the input sentence
  • " "_ remove/ignore spaces
  • w@.*' retrieve the first word beginning with each distinct character...
  • " "/ ...joining them together with spaces (to be implicitly returned)
answered Jul 14, 2021 at 18:13
\$\endgroup\$
1
\$\begingroup\$

Perl 5 -n, (削除) 44 (削除ここまで) 37 bytes

/./&&!$k{lc$&}++&&print$_,$"for/\w+/g

Try it online!

answered Nov 13, 2020 at 15:50
\$\endgroup\$
1
\$\begingroup\$

Japt v2.0a0 -S, (削除) 19 (削除ここまで) 16 bytes

f/\w+/
üÈÎvÃmÎnU

Try it or run all test cases

f/\w+/\nüÈÎvÃmÎnU :Implicit input of string U > "Ferulas flourish in gorgeous gardens."
f/\w+/ :Match /\w+/g > ["Ferulas","flourish","in","gorgeous","gardens"]
 \n :Reassign to U
 ü :Group and sort by
 È :Passing each through the following function
 Î : First character > ["F","f","i","g","g"]
 v : Lowercase > ["f","f","i","g","g"]
 Ã :End function > [["Ferulas","flourish"],["gorgeous","gardens"],["in"]]
 m :Map
 Î : First element > ["Ferulas","gorgeous","in"]
 nU :Sort by index in U > ["Ferulas","in","gorgeous"]
 :Implicit output joined with spaces > "Ferulas in gorgeous"
answered Nov 13, 2020 at 15:19
\$\endgroup\$
1
\$\begingroup\$

Vyxal S, 16 bytes

kr‛ _+↔⇩⌈:vhÞU*'

Try it Online!

Explanation:

kr‛ _+↔ # Remove any non A-Z,a-z,0-9,_, or space chars
 ⇩ # Lowercase
 ⌈ # Split on spaces
 : # Duplicate
 vh # Get the first letter of each
 ÞU # Nub Sieve (Unique mask)
 * # Multiply mask with list of words
 ' # Remove all empty strings
 # 'S' flag - join top of stack with spaces and print
answered Aug 5, 2021 at 15:05
\$\endgroup\$
1
\$\begingroup\$

Pip -s, 23 bytes

{YLC@ayNIl&lPBy}FIa@+XW

Attempt This Online!

Explanation

{YLC@ayNIl&lPBy}FIa@+XW
 a ; Command-line argument
 @ ; Find each regex match of
 + ; one or more consecutive
 XW ; word characters (alphanumeric + underscore)
{ }FI ; Filter the matches by this function:
 a ; The match
 @ ; First character
 LC ; Lowercased
 Y ; Store that in the y variable
 l ; List of unique first letters (initially empty)
 yNI ; Truthy if y is not in l, falsey otherwise
 & ; If truthy, then
 lPBy ; push y onto the list and return its new value
 ; (which is truthy because it's a nonempty list)
 ; Output, space-separated (-s flag)
answered Feb 27 at 22:45
\$\endgroup\$
1
\$\begingroup\$

Tcl, 133 bytes

proc F {s D\ {}} {lmap w $s {regsub -all \[^\\w] $w "" f
if {[set k [string tol [string in $f 0]]]ni$D} {dict se D $k $f}}
dict v $D}

Try it online!

answered Apr 27, 2018 at 13:08
\$\endgroup\$
1
\$\begingroup\$

Zsh, 72 bytes

for i (${@//[^0-9A-Za-z_]}){n=$i[1]:l;((${#n:|P}))&&printf $i\ ;P+=($n)}

Try it online!

Similar to my split string solution, but here we need more code to clean up the input.

answered Mar 4 at 8:08
\$\endgroup\$
1
\$\begingroup\$

05AB1E, 16 bytes

žjмS¡õK.¡нl}€нðý

Try it online or verify all test cases.

Explanation:

žj # Push constant "abc...xyzABC...XYZ012...789_"
 м # Remove all those characters from the (implicit) input-string
 S # Convert what remains into a list of characters
 ¡ # Split the (implicit) input-string by those characters
 õK # Remove all empty strings ""
 .¡ # Group all words/numbers/underscores by:
 н # Their first character
 l # Converted to lowercase
 }€н # After the group by: map over each group, and leave its first word
 ðý # Join the list with space delimiter
 # (after which the result is output implicitly)
answered Mar 4 at 8:42
\$\endgroup\$
1
\$\begingroup\$

Uiua, 26 bytes

/$"_ _"▽◰⌵⊸≡◇⊢⊢⍉regex$ \w+

Try it: Uiua pad

Without regex:

Uiua, 28 bytes

/$"_ _"▽◰⌵⊜⊃⊢しろいしかく↥⌵±⤙⊸∊⊂@_+@0⇡9

Try it: Uiua pad

answered Mar 4 at 12:20
\$\endgroup\$
0
\$\begingroup\$

Python 3.5, 138 bytes:

import re;lambda o,t=[]:''.join([y[0]for y in[(u+' ',t.append(u[0].lower()))for u in re.sub('\W+',' ',o).split()if u[0].lower()not in t]])

Basically, what's happening is..

  1. Using a simple regular expression, the program replaces all the characters, except lowercase or uppercase letters, digits, or underscores in the given string with spaces, and then splits the string at those spaces.
  2. Then, using list comprehension, create a list that iterates through all the words in the split string, and add the first letters of each word to list "t".
  3. In the process, if the current word's first letter is NOT already in the list "t", then that word and a trailing space are added to the current list being created. Otherwise, the list continues on appending the first letters of each word to list "t".
  4. Finally, when all words in the split have been iterated through, the words in the new list are joined into a string and returned.
answered Apr 7, 2016 at 9:31
\$\endgroup\$
1
2

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.