Trouvez le premier mot commençant par chaque lettre

25

Étant donné une chaîne, recherchez le premier mot commençant par chaque lettre (insensible à la casse).

Échantillon

Utilisation Ferulas flourish in gorgeous gardens.comme entrée:

"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`

Ensuite, la sortie de cet exemple doit être les mots correspondants joints par un seul espace:

"Ferulas in gorgeous"

Défi

L'entrée et la sortie doivent être une représentation sous forme de chaîne ou l'alternative la plus proche dans votre langue.

Programme ou fonction autorisé.

Vous pouvez considérer qu'un mot est au moins l'un des suivants lowercase or uppercase letters, digits, underscore:.

C'est le , la réponse la plus courte en victoires en octets.

Un autre échantillon:

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"
supprimé
la source
Les espaces de début / fin sont-ils autorisés? <s> Puis-je supposer que les mots sont séparés par un espace dans la chaîne d'origine? </s>
Qwertiy
Je l'ai compris à partir des exemples, il y a donc <s> </s> dans le commentaire. Qu'en est-il des espaces de coupe?
Qwertiy

Réponses:

17

Rétine , 28 octets:

M! I` \ b (\ w) (? <! \ B \ 1. +) \ W *
¶
 
  • M! - Faites correspondre chaque œuvre et imprimez tous les mots séparés par des retours à la ligne.
  • i - Ignorer la casse.
  • \b(\w) - Capturez la première lettre de chaque mot
  • (?<!\b\1.+)- Après avoir fait correspondre la lettre, vérifiez s'il n'y avait pas de mot précédent commençant par la même lettre. \1.+assure au moins deux caractères, donc nous sautons le mot actuel.
  • \w*- correspond au reste du mot.
    Ce qui précède ne correspond qu'aux mots - tous les autres caractères sont supprimés.
  • ¶\n - Remplacez les nouvelles lignes par des espaces.

Essayez-le en ligne!

Kobi
la source
9

Rétine , 45 octets

i` \ b ((\ w) \ w *) \ b (? <= \ b \ 2 \ w * \ b. +)

\ W +
 
^ | $

Utilise simplement une seule expression régulière pour supprimer les mots ultérieurs commençant par le même \wcaractère (insensible à la casse avec l' ioption), convertit les séquences de \Wen un seul espace, puis supprime tout espace de début / fin du résultat.

Essayez-le en ligne!

Edit: Voir la réponse de @ Kobi pour une version plus courte en utilisantM!`

Sp3000
la source
Bon sang, à peine me battre! Je ne pouvais pas comprendre le lookbehind cependant.
GamrCorps
3
J'ai ajouté une autre réponse Retina - je pense que c'est OK si elles sont suffisamment différentes (le concept de base est similaire, bien sûr).
Kobi
1
@Kobi C'est beaucoup mieux, donc je suis content de le voir :) Me fait réaliser à quel point j'ai besoin d'en savoir plus sur les options de la gamme Retina et ce qui ne l'est pas.
Sp3000
Pourriez-vous faire cela pour économiser quelques octets? i` \b((\w)\w*)\b(?<=\b\2\w*\b.+)(un espace avant le premier \b) Les lignes sont-elles ensuite inutiles?
Leaky Nun
@KennyLau Malheureusement, je ne pense pas que cela fonctionne parce que les mots ne sont pas nécessairement séparés par des espaces, par exemplea...a -> a
Sp3000
9

JavaScript (ES6), 73 71 octets

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

Enregistré 2 octets grâce à @ edc65!

Tester

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>");

user81655
la source
En utilisant parseInt("_",36) = NaN? Blasphème!
Sp3000
1
Le fait amusant est: cela fonctionne @ Sp3000
edc65
Utiliser u = regexp est vraiment intelligent. Économisez 2 octetss=>s.match(u=/\w+/g).filter(w=>u[w=parseInt(w[0],36)]?0:u[w]=1).join' '
edc65
@ edc65 Merci. Il est en fait assez pratique qu'il y ait 37 sorties possibles pour un seul chiffre de base 36.
user81655
7

Pyth, 23 octets

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

Essayez-le en ligne: démonstration ou suite de tests

J:z"\w+"1trouve tous les mots dans l'entrée à l'aide de l'expression rationnelle \w+et les stocke dans J.

.grhk0Jregroupe les mots par leur première lettre minuscule, hMprend la première de chaque groupe, xDJtrie ces mots par leur index dans la chaîne d'entrée et jdplace des espaces entre eux.

Jakube
la source
4

Perl 6, 39 octets

{.words.grep({!%.{.substr(0,1).lc}++})}
Ven
la source
1
42 octets qui corrigent les mots devant correspondre \w+et jouent la substrpartie
Jo King
3

C, 142 132 122 122 octets

10 octets de moins grâce à @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();}

Imprime un espace de fin après le dernier mot de sortie.

mIllIbyte
la source
1
vous pouvez raser les chèques pour c>47et c<58en utilisant isalnumau lieu deisalpha
tucuxi
3

MATL , 23 octets

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

Cela emprunte l'idée de Jakube d'utiliser une expression rationnelle pour supprimer les caractères indésirables et diviser en même temps.

L'entrée est une chaîne entre guillemets simples.

Essayez-le en ligne!

Explication

'\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
Luis Mendo
la source
2

Vim 57 touches

: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

Explication:

: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

Je suis vraiment déçu par la longueur de celui-ci. Les caractères non valides « » (de tout sauf a-z, A-Z, _et dans l' espace) vraiment jeté me off. Je suis sûr qu'il existe une meilleure façon de procéder:

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

Puisque \hcorrespond à tout cela, attendez-vous à l'espace, mais je ne peux pas comprendre comment mettre le métachar dans une plage. Si quelqu'un a des conseils, j'aimerais les entendre.

DJMcMayhem
la source
3
pourquoi a-zA-Z_et non \w? les chiffres sont valides
edc65
2

Julia, 165 155 151 129 102 octets

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)])," ")

Il s'agit d'une fonction qui accepte une chaîne et renvoie une chaîne.

Non golfé:

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

53 octets enregistrés avec l'aide de Sp3000!

Alex A.
la source
2

Gelée, 32 31 octets

ØB;”_
e€¢¬œṗf€¢¹ÐfµZḢŒlQi@€$ịj⁶

Essayez-le en ligne!

Dennis
la source
2

C # (LINQPAD) - 136 128 octets

var w=Util.ReadLine().Split(' ');string.Join(" ",w.Select(s=>w.First(f=>Regex.IsMatch(""+f[0],"(?i)"+s[0]))).Distinct()).Dump();
jzm
la source
2

05AB1E , 40 octets

Code:

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

Essayez-le en ligne!

Explication:

Nous générons d'abord tous les caractères qui doivent être supprimés de la chaîne d'entrée en utilisant 94L32+ç( Essayez ici ). Nous joignons cette chaîne en utilisant Jet en supprimant [a-zA-Z0-9_]qui est stockée dans žj ( Essayez ici ). Nous supprimons tous les caractères qui sont dans la deuxième chaîne de la première chaîne, ce qui nous laissera:

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

Cela peut également être testé ici . Nous le Dreproduisons et le stockons Xavec la Ucommande-. Nous supprimons ensuite tous les caractères de cette chaîne de l'entrée. Nous nous séparons ensuite sur les espaces blancs en utilisant ð¡et supprimons toutes les chaînes vides (en utilisant""K ). Nous avons maintenant cela .

Il s'agit de la version claire de l'entrée, avec laquelle nous travaillerons. Nous cartographions chaque élément en utilisant v. Cela utilise ycomme variable chaîne. Nous prenons le premier caractère de la chaîne en utilisant ¬et push X, qui contient une chaîne avec tous les caractères interdits ( !"#$%&'()*+,-./:;<=>?@[\]^`{|}~). Nous vérifions si la lversion en minuscule du premier caractère, (qui sera également ©affichée dans le registre), est dans cette chaîne en utilisant å. Couvert par cette partie:, ï>isi la première lettre n'existe pas dans la chaîne de caractères interdits (X ), nous ajoutons cette lettre à la liste des caractères interdits (terminé avec X®«U) et nous poussons yen haut de la pile.

Enfin, lorsque les chaînes sont filtrées, nous joignons la pile par des espaces avec ðý.

Adnan
la source
1
... explication? :-)
Luis Mendo
@LuisMendo Merci de me le rappeler! Fait :)
Adnan
2

PHP

Inspiré par l'utilisation de regex dans la plupart des réponses, j'ai essayé à l'origine de le faire sans utiliser regex du tout juste pour montrer une variation soignée, mais le point critique de ne pas avoir de chaînes propres en entrée a ruiné cette idée. Triste.

Avec wrapper de fonction, 89 octets

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

Sans wrapper de fonction (nécessitant $ s pré-déclaré), 73 octets

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

Explication:

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.

Mon seul regret est de ne pas avoir trouvé un moyen plus rapide de vérifier / convertir la casse des lettres.

Xanderhall
la source
2

Python, 103 octets

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]]
orlp
la source
1

Lua, 172 octets

Ça a fini bien plus longtemps que je voulais ...

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," "))

Non golfé

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
Katenkyo
la source
1

Sérieusement, 43 octets

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

Essayez-le en ligne!

Le manque de capacités d'expression régulière a rendu cela beaucoup plus difficile que nécessaire.

Explication:

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
Mego
la source
1

Ruby 76 octets

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

Ou avec une définition de méthode de 88 octets

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

Non golfé et avec test unitaire:

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
knut
la source
Dans Regex, \winclut des caractères numériques, [\w\d]peut donc être remplacé par \w. De plus, si les nilvaleurs sont dans un tableau lorsque vous appelez join' '(ou mieux encore, *' 'c'est un raccourci que vous pouvez utiliser pour économiser plus d'octets), elles disparaissent, donc l'appel à compactn'est pas nécessaire.
Value Ink
@KevinLau Merci. C'est \w\dgênant pour moi. Mais si je supprime le, compactj'obtiens des espaces supplémentaires (voir ['x',nil,'x']*'y' == 'xyyx'). Ou ai-je raté quelque chose?
knut
Oups, vous avez raison. Dans ce cas, (list-[p])enregistre les octets list.compact. Est également /\w/équivalent à /[\w]/. Enfin, vous pouvez remplacer votre nilavec pet votre !pavec 1(puisque votre hachage n'a besoin que de valeurs véridiques)
Value Ink
Merci, j'ai ajouté vos remarques, Le remplacement de nilpar pne fonctionne pas. Si je l'utilise dans mon code, j'obtiens une erreur de syntaxe. Je dois encapsuler comme (p)- mais j'ai encore 3 caractères.
knut
Retournez le ternaire et cela fonctionne pour sauver un octet: !f[j=i.upcase]?(f[j]=1;h):p. J'ai également pensé à cela, mais à cause de l'indexation des chaînes, l'utilisation s.scan(/\w+/)et la suppression de la ifaveur deh[0] travaux aussi.
Value Ink
1

grep et awk, 68 56 octets

Le script:

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

Explication:

  • grep -o correspond aux mots légaux, en les imprimant chacun sur sa propre ligne.

  • awkprend la première lettre de chaque ligne avec substr, la met en minuscule, puis incrémente une entrée de table de hachage avec cette clé. Si la valeur n'a pas été définie avant l'incrément, la ligne est imprimée.

  • echo ... transforme les lignes en mots

J'ai déjà essayé de créer une solution sans awk , en utilisant uniq, sort, grepet , bashmais a manqué de peu. Histoire dans les éditions.

Merci à Dennis pour certaines améliorations que j'ai ratées.

joeytwiddle
la source
0

Python 3.5, 138 octets:

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]])

Fondamentalement, ce qui se passe est ..

  1. À l'aide d'une expression régulière simple, le programme remplace tous les caractères, à l'exception des lettres minuscules ou majuscules, des chiffres ou des traits de soulignement dans la chaîne donnée par des espaces, puis divise la chaîne à ces espaces.
  2. Ensuite, à l'aide de la compréhension de liste, créez une liste qui parcourt tous les mots de la chaîne fractionnée et ajoutez les premières lettres de chaque mot à la liste "t".
  3. Dans le processus, si la première lettre du mot actuel n'est PAS déjà dans la liste "t", alors ce mot et un espace de fin sont ajoutés à la liste actuelle en cours de création. Sinon, la liste continue en ajoutant les premières lettres de chaque mot à la liste "t".
  4. Enfin, lorsque tous les mots de la division ont été itérés, les mots de la nouvelle liste sont joints dans une chaîne et renvoyés.
R. Kap
la source
0

PHP 120 octets

function a($s){foreach(preg_split('/\W/',$s)as$w)if(!$o[ucfirst($w[0])]){$o[ucfirst($w[0])]=$w;}return implode(" ",$o);}

Cela génère un tas d'avertissements, mais c'est très bien.

user52869
la source
Est-ce functionnécessaire?
AL
0

Javascript ES6, 108 107 caractères

107 caractères, la chaîne de résultat est tronquée

r=s=>s.split``.reverse().join``
f=s=>r(r(s).replace(/\b\w*(\w)\b(?=.*\1\b)/gi,'')).replace(/\W+/g,' ').trim()

Tester:

["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"
].map(f) + '' == [
"Take all first words each letter is",
"Look _ There are 3 dogs",
"maybe some day 1 plus 2 could result in 3"
]
Qwertiy
la source
f= ne compte pas .
Qwertiy
0

Tcl , 150 octets

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

Essayez-le en ligne!

sergiol
la source