Encontre a primeira palavra que começa com cada letra

25

Dada uma sequência, encontre a primeira palavra começando com cada letra (sem distinção entre maiúsculas e minúsculas).

Amostra

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

Em seguida, a saída para esta amostra deve ser as palavras correspondentes unidas por um único espaço:

"Ferulas in gorgeous"

Desafio

Tanto a entrada como a saída devem ser uma representação de sequência ou a alternativa mais próxima no seu idioma.

Programa ou função permitida.

Você pode considerar uma palavra sendo pelo menos um dos seguintes: lowercase or uppercase letters, digits, underscore.

Este é o , a resposta mais curta em bytes ganha.

Outras amostras:

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"
removido
fonte
Os espaços de fuga / partida são permitidos? <s> Posso assumir palavras são separadas por um espaço na string original </ s>?
Qwertiy
Entendi isso a partir dos exemplos, então há <s> </s> no comentário. E quanto a espaços para aparar?
Qwertiy

Respostas:

17

Retina , 28 bytes:

M! I` \ b (\ w) (? <! \ B \ 1. +) \ W *
¶
 
  • M! - Combine cada trabalho e imprima todas as palavras separadas por novas linhas.
  • i - Ignorar caso.
  • \b(\w) - Capture a primeira letra de cada palavra
  • (?<!\b\1.+)- Depois de corresponder à letra, verifique se não havia uma palavra anterior começando com a mesma letra. \1.+garante pelo menos dois caracteres, por isso estamos pulando a palavra atual.
  • \w*- combine o resto da palavra.
    O texto acima corresponde apenas às palavras - todos os outros caracteres são removidos.
  • ¶\n - Substitua novas linhas por espaços.

Experimente online!

Kobi
fonte
9

Retina , 45 bytes

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

\ W +
 
^ | $

Simplesmente usa uma única expressão regular para remover as palavras posteriores que começam com o mesmo \wcaractere (sem distinção entre maiúsculas e minúsculas com a iopção), converte execuções \Wem um único espaço e remove qualquer espaço inicial / final do resultado.

Experimente online!

Edit: Veja a resposta de @ Kobi para uma versão mais curta usandoM!`

Sp3000
fonte
Droga, mal me derrotei! Eu não conseguia descobrir o que estava por trás.
GamrCorps
3
Adicionei outra resposta Retina - acho que tudo bem se forem diferentes o suficiente (o conceito básico é semelhante, é claro).
Kobi
1
@Kobi Está muito melhor, então fico feliz em vê-lo :) Me faz perceber o quanto mais preciso aprender sobre as opções de linha da Retina e o que não.
SP3000
Você poderia fazer isso para salvar alguns bytes? i` \b((\w)\w*)\b(?<=\b\2\w*\b.+)(um espaço antes do primeiro \b) As linhas depois são desnecessárias?
Freira vazada
@KennyLau Infelizmente, eu não acho que as obras porque as palavras não são necessariamente separadas por espaços, por exemploa...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` `

Economizou 2 bytes graças a @ edc65!

Teste

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
fonte
Usando parseInt("_",36) = NaN? Blasfêmia!
SP3000
1
O fato de diversão é: ele funciona @ SP3000
edc65
Usar u = regexp é realmente inteligente. Salve 2 bytess=>s.match(u=/\w+/g).filter(w=>u[w=parseInt(w[0],36)]?0:u[w]=1).join' '
edc65
@ edc65 Obrigado. Na verdade, é bastante conveniente que existam 37 saídas possíveis para um único dígito de base 36.
precisa saber é o seguinte
7

Pitão, 23 bytes

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

Experimente on-line: Demonstration or Test Suite

J:z"\w+"1localiza todas as palavras na entrada usando o regex \w+e as armazena J.

.grhk0Jagrupa as palavras pela primeira letra minúscula, hMpega a primeira de cada grupo, xDJclassifica essas palavras pelo índice na sequência de entrada e jdcoloca espaços entre elas.

Jakube
fonte
4

Perl 6, 39 bytes

{.words.grep({!%.{.substr(0,1).lc}++})}
Ven
fonte
1
42 bytes que corrige as palavras que têm que corresponder \w+e golfs a substrparte
Jo rei
3

C, 142 132 122 bytes

10 bytes mais leve, graças 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 um espaço à direita após a última palavra de saída.

mIllIbyte
fonte
1
você pode raspar as verificações para c>47e c<58usando isalnumem vez deisalpha
tucuxi
3

MATL , 23 bytes

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

Isso empresta a idéia de Jakube de usar um regexp para remover caracteres indesejados e dividir ao mesmo tempo.

Entrada é uma cadeia de caracteres entre aspas simples.

Experimente online!

Explicação

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

Teclas do Vim 57

: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

Explicação:

: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

Estou realmente desapontado com a duração deste. Os caracteres "inválidos" (tudo, mas a-z, A-Z, _e no espaço) realmente me jogou fora. Tenho certeza de que há uma maneira melhor de fazer isso:

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

Desde que \hcorresponde a tudo o que se espera do espaço, mas não consigo descobrir como colocar o metachar em um intervalo. Se alguém tiver dicas, eu adoraria ouvi-las.

DJMcMayhem
fonte
3
porque a-zA-Z_e não \w? dígitos são válidos
edc65 7/16
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 é uma função que aceita uma string e retorna uma 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

53 bytes salvos com a ajuda do Sp3000!

Alex A.
fonte
2

Geléia, 32 31 bytes

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

Experimente online!

Dennis
fonte
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
fonte
2

05AB1E , 40 bytes

Código:

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

Experimente online!

Explicação:

Primeiro, geramos todos os caracteres que devem ser excluídos da string de entrada usando 94L32+ç( Experimente aqui ). Juntamos essa string usando Je removendo o [a-zA-Z0-9_]que é armazenado em jj ( Tente aqui ). Removemos todos os caracteres que estão na segunda sequência da primeira, o que nos deixará:

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

Isso também pode ser testado aqui . Nós Dduplicamos isso e armazenamos no Xcom o Ucomando-. Em seguida, removemos todos os caracteres que estão nessa cadeia de caracteres da entrada. Em seguida, dividimos nos espaços em branco usando ð¡e removendo todas as cadeias vazias (usando ""K). Agora temos isso .

Esta é a versão limpa da entrada, com a qual trabalharemos. Mapeamos cada elemento usando v. Isso usa ycomo a variável string. Pegamos o primeiro caractere da string usando ¬e push X, que contém uma string com todos os caracteres proibidos ( !"#$%&'()*+,-./:;<=>?@[\]^`{|}~). Verificamos se a lversão owercase do primeiro caractere (que também será ©usado para o registro) está nessa string usando å. Abrangido por esta parte: ï>ise a primeira letra não existir na sequência de caracteres proibidos ( X), anexamos esta carta à lista de caracteres proibidos (terminada com X®«U) e empurramos o ytopo da pilha.

Finalmente, quando as strings são filtradas, juntamos a pilha por espaços com ðý.

Adnan
fonte
1
... explicação? :-)
Luis Mendo
@LuisMendo Obrigado por me lembrar! Feito :)
Adnan
2

PHP

Inspirado pelo uso de regex na maioria das respostas, originalmente tentei fazer isso sem usar regex, apenas para mostrar uma variação elegante, mas o ponto difícil de não ter strings limpas como entrada arruinou essa idéia. Triste.

Com wrapper de função, 89 bytes

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

Sem wrapper de função (que precisa de $ s pré-declarado), 73 bytes

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

Explicação:

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.

Meu único arrependimento é que não consegui encontrar uma maneira mais rápida de verificar / converter letras maiúsculas.

Xanderhall
fonte
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
fonte
1

Lua, 172 bytes

Acabou muito mais tempo que eu queria ...

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
Katenkyo
fonte
1

Sério, 43 bytes

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

Experimente online!

A falta de recursos de regex tornou isso muito mais difícil do que precisava ser.

Explicação:

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

Ruby 76 bytes

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

Ou com definição de 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

Ungolfed e com teste de unidade:

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
fonte
No Regex, \winclui caracteres numéricos, portanto, [\w\d]pode ser substituído por \w. Além disso, se os nilvalores estiverem em uma matriz quando você ligar join' '(ou melhor ainda, *' 'for uma abreviação que você pode usar para salvar mais bytes), eles desaparecerão, portanto a chamada para compacté desnecessária.
Value Ink
@KevinLau Thanks. O \w\dé constrangedor para mim. Mas se eu remover o compactespaço, recebo mais espaços (consulte ['x',nil,'x']*'y' == 'xyyx'). Ou eu perdi alguma coisa?
Knut
Opa, você está certo. Nesse caso, (list-[p])salva bytes em excesso list.compact. Além disso, /\w/é equivalente a /[\w]/. Finalmente, você pode substituir o seu nilcom pe seu !pcom 1(desde que seu hash somente precisa de valores truthy nele)
Valor Ink
Obrigado, eu adicionei seus comentários, A substituição de nilpor pnão funciona. Se eu usá-lo dentro do meu código, recebo um erro de sintaxe. Eu tenho que encapsular como (p)- mas então eu tenho novamente 3 caracteres.
Knut
Virar o ternário e, em seguida, ele funciona para salvar um byte: !f[j=i.upcase]?(f[j]=1;h):p. Também pensei nisso, mas por causa da indexação de strings, usando s.scan(/\w+/)e removendo o ifavor de h[0]obras também.
Value Ink
1

grep e awk, 68 56 bytes

O script:

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

Explicação:

  • grep -o corresponde às palavras legais, imprimindo cada uma em sua própria linha.

  • awkpega a primeira letra de cada linha substr, torna-a em minúscula e depois incrementa uma entrada de hashtable com essa chave. Se o valor não tiver sido definido antes do incremento, a linha será impressa.

  • echo ... transforma as linhas de volta em palavras

Eu já tentei criar uma solução sem awk, usando uniq, sort, grepe bashmas caiu um pouco abaixo. História nas edições.

Agradeço ao Dennis por algumas melhorias que perdi.

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

Basicamente, o que está acontecendo é ..

  1. Usando uma expressão regular simples, o programa substitui todos os caracteres, exceto letras minúsculas ou maiúsculas, dígitos ou sublinhados na sequência especificada por espaços e, em seguida, divide a sequência nesses espaços.
  2. Em seguida, usando a compreensão da lista, crie uma lista que itere por todas as palavras na cadeia de caracteres dividida e adicione as primeiras letras de cada palavra à lista "t".
  3. No processo, se a primeira letra da palavra atual NÃO estiver na lista "t", essa palavra e um espaço à direita serão adicionados à lista atual que está sendo criada. Caso contrário, a lista continuará anexando as primeiras letras de cada palavra à lista "t".
  4. Finalmente, quando todas as palavras da divisão tiverem sido iteradas, as palavras da nova lista serão unidas em uma sequência e retornadas.
R. Kap
fonte
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);}

Isso gera vários avisos, mas tudo bem.

user52869
fonte
É functionnecessário?
AL
0

Javascript ES6, 108 107 caracteres

107 caracteres, a sequência de resultados é aparada

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

Teste:

["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
fonte
f= não conta .
precisa saber é o seguinte
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}

Experimente online!

sergiol
fonte