Encuentra la primera palabra que comienza con cada letra

25

Dada una cadena, encuentre la primera palabra que comienza con cada letra (sin distinción entre mayúsculas y minúsculas).

Muestra

Utilizando Ferulas flourish in gorgeous gardens.como entrada:

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

Entonces, el resultado de esta muestra debe ser las palabras coincidentes unidas por un solo espacio:

"Ferulas in gorgeous"

Reto

Tanto la entrada como la salida deben ser una representación de cadena o la alternativa más cercana en su idioma.

Programa o función permitida.

Se puede considerar una palabra siendo al menos uno de: lowercase or uppercase letters, digits, underscore.

Este es el , la respuesta más corta en bytes gana.

Otras muestras:

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"
remoto
fuente
¿Se permiten espacios finales / iniciales? <s> ¿Puedo suponer que las palabras están separadas por un espacio en la cadena original? </s>
Qwertiy
Lo entendí de los ejemplos, por lo que hay <s> </s> en el comentario. ¿Qué hay de recortar espacios?
Qwertiy

Respuestas:

17

Retina , 28 bytes:

M! I` \ b (\ w) (? <! \ B \ 1. +) \ W *
¶
 
  • M! - Unir cada trabajo e imprimir todas las palabras separadas por nuevas líneas.
  • i - Ignorar caso.
  • \b(\w) - Capture la primera letra de cada palabra
  • (?<!\b\1.+)- Después de hacer coincidir la letra, verifique si no había una palabra anterior que comenzara con la misma letra. \1.+asegura al menos dos caracteres, por lo que estamos omitiendo la palabra actual.
  • \w*- coincide con el resto de la palabra.
    Lo anterior solo coincide con las palabras: todos los demás caracteres se eliminan.
  • ¶\n - Reemplazar nuevas líneas con espacios.

Pruébalo en línea!

Kobi
fuente
9

Retina , 45 bytes

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

\ W +
 
^ | PS

Simplemente usa una única expresión regular para eliminar palabras posteriores que comienzan con el mismo \wcarácter (no distingue entre mayúsculas y minúsculas con la iopción), convierte las ejecuciones \Wen un solo espacio, luego elimina cualquier espacio inicial / final del resultado.

Pruébalo en línea!

Editar: Vea la respuesta de @ Kobi para una versión más corta usandoM!`

Sp3000
fuente
¡Maldición, apenas me gané! Sin embargo, no pude entender la mirada atrás.
GamrCorps
3
He agregado otra respuesta de Retina: creo que está bien si son lo suficientemente diferentes (el concepto básico es similar, por supuesto).
Kobi
1
@Kobi Es mucho mejor, así que me alegra verlo :) Me hace darme cuenta de cuánto más necesito aprender sobre las opciones de línea de Retina y qué no.
Sp3000
¿Podría hacer esto para guardar algunos bytes? i` \b((\w)\w*)\b(?<=\b\2\w*\b.+)(un espacio antes del primero \b) ¿Son innecesarias las líneas posteriores?
Leaky Nun
@KennyLau Desafortunadamente, no creo que funcione porque las palabras no están necesariamente separadas por espacios, por ejemploa...a -> a
Sp3000
9

JavaScript (ES6), 73 71 bytes

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

¡Ahorré 2 bytes gracias a @ edc65!

Prueba

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

usuario81655
fuente
Utilizando parseInt("_",36) = NaN? ¡Blasfemia!
Sp3000
1
El hecho divertido es: funciona @ Sp3000
edc65
Usar u = regexp es realmente inteligente. Ahorre 2 bytess=>s.match(u=/\w+/g).filter(w=>u[w=parseInt(w[0],36)]?0:u[w]=1).join' '
edc65
@ edc65 Gracias. En realidad, es bastante conveniente que haya 37 salidas posibles para una sola base de 36 dígitos.
user81655
7

Pyth, 23 bytes

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

Pruébelo en línea: Demostración o conjunto de pruebas

J:z"\w+"1encuentra todas las palabras en la entrada usando la expresión regular \w+y las almacena J.

.grhk0Jagrupa las palabras por su primera letra minúscula, hMtoma la primera de cada grupo, xDJclasifica estas palabras por su índice en la cadena de entrada y jdcoloca espacios entre ellas.

Jakube
fuente
4

Perl 6, 39 bytes

{.words.grep({!%.{.substr(0,1).lc}++})}
Ven
fuente
1
42 bytes que corrige las palabras que tienen que coincidir \w+y juega golf en la substrparte
Jo King
3

C, 142 132 122 bytes

¡10 bytes más ligero gracias a @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 espacio final después de la última palabra de salida.

milIbyte
fuente
1
puede afeitarse los cheques c>47y c<58usarlos en isalnumlugar deisalpha
tucuxi
3

MATL , 23 bytes

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

Esto toma prestada la idea de Jakube de usar una expresión regular para eliminar caracteres no deseados y dividirlos al mismo tiempo.

La entrada es una cadena entre comillas simples.

Pruébalo en línea!

Explicación

'\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
fuente
2

Vim 57 pulsaciones de teclas

: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

Explicación:

: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

Estoy realmente decepcionado por lo largo que es este. Los caracteres "no válido" (todo pero a-z, A-Z, _y el espacio) realmente me tiró fuera. Estoy seguro de que hay una mejor manera de hacer esto:

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

Dado que \hcoincide con todo lo que se espera para el espacio, pero no puedo entender cómo poner el metachar en un rango. Si alguien tiene consejos, me encantaría escucharlos.

DJMcMayhem
fuente
3
¿Por qué a-zA-Z_no \w? los dígitos son válidos
edc65
2

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

Esta es una función que acepta una cadena y devuelve una cadena.

Sin 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

¡Ahorró 53 bytes con ayuda de Sp3000!

Alex A.
fuente
2

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();
jzm
fuente
2

05AB1E , 40 bytes

Código:

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

Pruébalo en línea!

Explicación:

Primero generamos todos los caracteres que deben eliminarse de la cadena de entrada usando 94L32+ç( Probar aquí ). Unimos esta cadena usando Jy [a-zA-Z0-9_]eliminamos el que está almacenado en žj ( Pruébelo aquí ). Eliminamos todos los caracteres que están en la segunda cadena de la primera cadena, lo que nos dejará:

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

Eso también se puede probar aquí . Lo Dduplicamos y lo almacenamos Xcon el Ucomando. Luego eliminamos todos los caracteres que están en esta cadena de la entrada. Luego nos dividimos en espacios en blanco usando ð¡y eliminamos todas las cadenas vacías (usando ""K). Ahora tenemos esto .

Esta es la versión limpia de la entrada, con la que trabajaremos. Mapeamos cada elemento usando v. Esto se usa ycomo la variable de cadena. Tomamos el primer carácter de la cadena usando ¬y push X, que contiene una cadena con todos los caracteres prohibidos ( !"#$%&'()*+,-./:;<=>?@[\]^`{|}~). Verificamos si la lversión en minúscula del primer carácter (que también se ©optará al registro) está en esta cadena usando å. Cubierto por esta parte: ï>isi la primera letra no existe en la cadena de caracteres prohibidos ( X), agregamos esta letra a la lista de caracteres prohibidos (hecho con X®«U) y presionamos yen la parte superior de la pila.

Finalmente, cuando las cadenas se filtran, unimos la pila por espacios con ðý.

Adnan
fuente
1
... explicación? :-)
Luis Mendo
@LuisMendo ¡Gracias por recordármelo! Hecho :)
Adnan
2

PHP

Inspirado por el uso de expresiones regulares en la mayoría de las respuestas, originalmente intenté hacer esto sin usar expresiones regulares solo para mostrar una variación ordenada, pero el problema de no tener cadenas limpias como entrada arruinó esa idea. Triste.

Con contenedor de funciones, 89 bytes

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

Sin envoltorio de función (que necesita $ s pre-declarado), 73 bytes

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

Explicación:

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.

Lo único que lamento es que no pude encontrar una forma más rápida de verificar / convertir mayúsculas y minúsculas.

Xanderhall
fuente
2

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]]
orlp
fuente
1

Lua, 172 bytes

Terminó mucho más tiempo de lo que quería ...

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

Sin 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
fuente
1

En serio, 43 bytes

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

Pruébalo en línea!

La falta de capacidades de expresiones regulares hizo que esto fuera mucho más difícil de lo necesario.

Explicación:

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
fuente
1

Ruby 76 Bytes

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

O con la definición del método 88 bytes

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

Sin golf y con prueba unitaria:

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
fuente
En Regex, \wincluye caracteres numéricos, por lo que [\w\d]se puede reemplazar con \w. Además, si los nilvalores están en una matriz cuando llama join' '(o mejor aún, *' 'es una abreviatura que puede usar para guardar más bytes), desaparecen, por lo que la llamada a compactes innecesaria.
Value Ink el
@KevinLau Gracias. El \w\des vergonzoso para mí. Pero si elimino el compact, obtengo espacios adicionales, (ver ['x',nil,'x']*'y' == 'xyyx'). ¿O me perdí algo?
knut
Vaya, tienes razón. En ese caso, (list-[p])guarda los bytes list.compact. Además, /\w/es equivalente a /[\w]/. Finalmente, puede reemplazar su nilcon py su !pcon 1(ya que su hash solo necesita valores verdaderos)
Value Ink
Gracias, agregué sus comentarios, El reemplazo de nilcon pno funciona. Si lo uso dentro de mi código me sale un error de sintaxis. Tengo que encapsular como (p), pero luego vuelvo a tener 3 caracteres.
knut
Da la vuelta al ternario y entonces funciona para guardar un byte: !f[j=i.upcase]?(f[j]=1;h):p. También pensé en esto, pero debido a la indexación de cadenas, también usamos s.scan(/\w+/)y eliminamos los trabajos a ifavor h[0].
Value Ink el
1

grep y awk, 68 56 bytes

La secuencia de comandos:

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

Explicación:

  • grep -o coincide con las palabras legales, imprimiendo cada una en su propia línea.

  • awktoma la primera letra de cada línea con substr, la pone en minúscula y luego incrementa una entrada de tabla hash con esa tecla. Si el valor no se estableció antes del incremento, se imprime la línea.

  • echo ... convierte las líneas de nuevo en palabras

Me previamente tratado de crear una solución sin awk, con uniq, sort, grepy bashaunque se quedó corto. Historia en las ediciones.

Gracias a Dennis por algunas mejoras que perdí.

joeytwiddle
fuente
0

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

Básicamente, lo que está sucediendo es ...

  1. Usando una expresión regular simple, el programa reemplaza todos los caracteres, excepto letras minúsculas o mayúsculas, dígitos o guiones bajos en la cadena dada con espacios, y luego divide la cadena en esos espacios.
  2. Luego, utilizando la comprensión de la lista, cree una lista que recorra en iteración todas las palabras de la cadena dividida y agregue las primeras letras de cada palabra a la lista "t".
  3. En el proceso, si la primera letra de la palabra actual NO está ya en la lista "t", entonces esa palabra y un espacio final se agregarán a la lista actual que se está creando. De lo contrario, la lista continúa agregando las primeras letras de cada palabra a la lista "t".
  4. Finalmente, cuando todas las palabras en la división se han iterado, las palabras en la nueva lista se unen en una cadena y se devuelven.
R. Kap
fuente
0

PHP 120bytes

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

Esto genera un montón de advertencias, pero está bien.

usuario52869
fuente
Es lo functionnecesario?
AL
0

Javascript ES6, 108 107 caracteres

107 caracteres, la cadena de resultado se recorta

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

Prueba:

["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
fuente
f= no cuenta .
Qwertiy
0

Tcl , 150 bytes

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}

Pruébalo en línea!

sergiol
fuente