Remover duplicatas de uma String

17

Inspirado por essa pergunta despretensiosa do StackOverflow .

A ideia é simples; dado uma String e uma matriz de Strings, remova quaisquer instâncias de palavras na matriz (ignorando maiúsculas e minúsculas) da String de entrada diferente da primeira, juntamente com qualquer espaço em branco adicional que isso possa deixar. As palavras devem corresponder a palavras inteiras na String de entrada, e não partes das palavras.

por exemplo, "A cat called matt sat on a mat and wore a hat A cat called matt sat on a mat and wore a hat", ["cat", "mat"]deve produzir"A cat called matt sat on a mat and wore a hat A called matt sat on a and wore a hat"

Entrada

  • A entrada pode ser tomada como uma String e uma matriz de Strings ou uma matriz de Strings em que a String de entrada é o primeiro elemento. Esses parâmetros podem estar em qualquer ordem.
  • A entrada String não pode ser considerada uma lista de Strings delimitadas por espaço.
  • A String de entrada não terá espaços à esquerda, à direita ou consecutivos.
  • Toda entrada conterá apenas caracteres [A-Za-z0-9], com exceção da String de entrada, incluindo também espaços.
  • A matriz de entrada pode estar vazia ou conter palavras que não estão na String de entrada.

Resultado

  • A saída pode ser o valor de retorno de uma função ou impressa em STDOUT
  • A saída deve estar no mesmo caso que a String original

Casos de teste

the blue frog lived in a blue house, [blue] -> the blue frog lived in a house
he liked to read but was filled with dread wherever he would tread while he read, [read] -> he liked to read but was filled with dread wherever he would tread while he
this sentence has no matches, [ten, cheese] -> this sentence has no matches
this one will also stay intact, [] -> this one will also stay intact
All the faith he had had had had no effect on the outcome of his life, [had] -> All the faith he had no effect on the outcome of his life
5 times 5 is 25, [5, 6] -> 5 times is 25
Case for different case, [case] -> Case for different
the letters in the array are in a different case, [In] -> the letters in the array are a different case
This is a test Will this be correct Both will be removed, [this,will] -> This is a test Will be correct Both be removed

Como este é um código de golfe, a contagem de bytes mais baixa ganha!

Luke Stevens
fonte

Respostas:

9

R , 84 bytes

function(s,w,S=el(strsplit(s," ")),t=tolower)cat(S[!duplicated(x<-t(S))|!x%in%t(w)])

Experimente online!

Menos de 100 bytes em um desafio de que também não é ?

Explicação:

Depois de dividirmos a sequência em palavras, precisamos excluir as que são

  1. duplicatas e
  2. dentro w

ou, alternativamente, virar isso de cabeça para baixo, mantendo aqueles que são

  1. a primeira ocorrência de uma palavra OU
  2. não em w.

duplicatedordenadamente retorna índices lógicos daqueles que não são a primeira ocorrência, de modo !duplicated()retorna índices daqueles que são as primeiras ocorrências, e x%in%wretorna índices lógicos para xaqueles que estão em w. Arrumado.

Giuseppe
fonte
6

Java 8, 117 110 bytes

a->s->{for(String x:a)for(x="(?i)(.*"+x+".* )"+x+"( |$)(.*)";s.matches(x);s=s.replaceAll(x,"$1$3"));return s;}

Explicação:

Experimente online.

a->s->{                // Method with String-array and String parameters and String return
  for(String x:a)      //  Loop over the input-array
    for(x="(?i)(.*"+x+".* )"+x+"( |$)(.*)";
                       //   Regex to match
        s.matches(x);  //   Inner loop as long as the input matches this regex
      s=s.replaceAll(x,"$1$3")); 
                       //    Replace the regex-match with the 1st and 3rd capture groups
  return s;}           //  Return the modified input-String

Explicação adicional para a regex:

(?i)(.*"+x+".* )"+x+"( |$)(.*)   // Main regex to match:
(?i)                             //  Enable case insensitivity
    (                            //  Open capture group 1
     .*                          //   Zero or more characters
       "+x+"                     //   The input-String
            .*                   //   Zero or more characters, followed by a space
               )                 //  End of capture group 1
                "+x+"            //  The input-String again
                     (           //  Open capture group 2
                       |$        //   Either a space or the end of the String
                         )       //  End of capture group 2
                          (      //  Open capture group 3
                           .*    //   Zero or more characters
                             )   //  End of capture group 3

$1$3                             // Replace the entire match with:
$1                               //  The match of capture group 1
  $3                             //  concatted with the match of capture group 3
Kevin Cruijssen
fonte
4

MATL , 19 18 bytes

"Ybtk@kmFyfX<(~)Zc

As entradas são: uma matriz de células de strings e, em seguida, uma string.

Experimente online! Ou verifique todos os casos de teste .

Como funciona

"        % Take 1st input (implicit): cell array of strings. For each
  Yb     %   Take 2nd input (implicit) in the first iteration: string; or
         %   use the string from previous iteration. Split on spaces. Gives
         %   a cell array of strings
  tk     %   Duplicate. Make lowercase
  @k     %   Push current string from the array taken as 1st input. Make
         %   lowercase
  m      %   Membership: gives true-false array containing true for strings
         %   in the first input argument that equal the string in the second
         %   input argument
  F      %   Push false
  y      %   Duplicate from below: pushes the true-false array again
  f      %   Find: integer indices of true entries (may be empty)
  X<     %   Minimum (may be empty)
  (      %   Assignment indexing: write false in the true-false array at that
         %   position. So this replaces the first true (if any) by false
  ~      %   Logical negate: false becomes true, true becomes false
  )      %   Reference indexing: in the array of (sub)strings that was
         %   obtained from the second input, keep only those indicated by the
         %   (negated) true-false array
  Zc     %   Join strings in the resulting array, with a space between them
         % End (implicit). Display (implicit)
Luis Mendo
fonte
3

Perl 5 , 49 bytes

@B=<>;$_=join$",grep!(/^$_$/xi~~@B&&$v{+lc}++),@F

Experimente online!

Guardou 9 (!!) bytes graças a @TonHospel !

Dom Hastings
fonte
1
Isso parece falhar no This is a test Will this be correct Both will be removed+ this will. As duas segundas palavras foram removidas corretamente, mas também removeram a beapós a segunda willpor algum motivo.
21918 Kevin Cruijssen
1
@KevinCruijssen Hmmm, posso ver por que isso está acontecendo no momento. Amanhã vou tentar dar uma boa olhada no almoço, mas resolvi por enquanto a um custo de +4. Obrigado por me avisar!
Dom Hastings
Para 49:@B=<>;$_=join$",grep!(/^$_$/xi~~@B&&$v{+lc}++),@F
Ton Hospel
@TonHospel Ahh, passei um tempo tentando lcser chamado sem parênteses. Impressionante! E usar um regex contra a matriz é muito melhor, obrigado! Eu luto para lembrar todas as suas dicas!
Dom Hastings
2

Pitão, 27 bytes

jdeMf!}r0eT@mr0dQmr0dPT._cz

Experimente online

Explicação

jdeMf!}r0eT@mr0dQmr0dPT._cz
                          z  Take the string input.
                       ._c   Get all the prefixes...
    f    eT@                 ... which end with something...
     !}         Q    PT      ... which is not in the input and the prefix...
       r0   mr0d mr0d        ... case insensitive.
jdeM                         Join the ends of each valid prefix.

Tenho certeza de que os 10 bytes para verificação sem distinção entre maiúsculas e minúsculas podem ser reduzidos, mas não vejo como.


fonte
2

Stax , 21 bytes CP437

åìøΓ²¬$M¥øHΘQä~╥ôtΔ♫╟

25 bytes quando descompactado,

vjcm[]Ii<;e{vm_]IU>*Ciyj@

O resultado é uma matriz. A saída conveniente para Stax é um elemento por linha.

Execute e depure online!

Explicação

vj                           Convert 1st input to lowercase and split at spaces,
  c                          Duplicate at the main stack
   m                         Map array with the rest of the program 
                                 Implicitly output
    []I                      Get the first index of the current array element in the array
       i<                    Test 1: The first index is smaller than the iteration index
                                 i.e. not the first appearance
         ;                   2nd input
          {vm                Lowercase all elements
             _]I             Index of the current element in the 2nd input (-1 if not found)
                U>           Test 2: The index is non-negative
                                 i.e. current element is a member of the 2nd input
                  *C         If test 1 and test 2, drop the current element
                                 and go on mapping the next
                    iyj@     Fetch the corresponding element in the original input and return it as the mapped result
                                 This preserves the original case
Weijun Zhou
fonte
2

Perl 6 , 49 bytes

->$_,+w{~.words.grep:{.lcw».lc||!(%){.lc}++}}

Teste-o

Expandido:

->              # pointy block lambda
  $_,           # first param 「$_」 (string)
  +w            # slurpy second param 「w」 (words)
{

  ~             # stringify the following (joins with spaces)

  .words        # split into words (implicit method call on 「$_」)

  .grep:        # take only the words we want

   {
     .lc        # lowercase the word being tested
               # is it not an element of
     w».lc      # the list of words, lowercased

     ||         # if it was one of the words we need to do a secondary check

     !          # Boolean invert the following
                # (returns true the first time the word was found)

     (
       %        # anonymous state Hash variable
     ){ .lc }++ # look up with the lowercase of the current word, and increment
   }
}
Brad Gilbert b2gills
fonte
2

Perl 5 , 50 bytes

Inclui +1para-p

Dê a sequência de destino seguida por cada palavra de filtro em linhas separadas em STDIN:

perl -pe '$"="|";s%\b(@{[<>]})\s%$&x!$v{lc$1}++%iegx;chop';echo
This is a test Will this be correct Both will be removed
this
will
^D
^D

A chopsó é necessária para corrigir o espaço à direita no caso da última palavra é removido

Apenas o código:

$"="|";s%\b(@{[<>]})\s%$&x!$v{lc$1}++%iegx;chop

Experimente online!

Ton Hospel
fonte
1

JavaScript (ES6), 98 bytes

s=>a=>s.split` `.filter(q=x=>(q[x=x.toLowerCase()]=eval(`/\\b${x}\\b/i`).test(a)<<q[x])<2).join` `
ETHproductions
fonte
1

K4 , 41 bytes

Solução:

{" "/:x_/y@>y:,/1_'&:'(_y)~/:\:_x:" "\:x}

Exemplos:

q)k){" "/:x_/y@>y:,/1_'&:'(_y)~/:\:_x:" "\:x}["A cat called matt sat on a mat and wore a hat A cat called matt sat on a mat and wore a hat";("cat";"mat")]
"A cat called matt sat on a mat and wore a hat A called matt sat on a and wore a hat"

q)k){" "/:x_/y@>y:,/1_'&:'(_y)~/:\:_x:" "\:x}["Case for different case";enlist "case"]
"Case for different"

q)k){" "/:x_/y@>y:,/1_'&:'(_y)~/:\:_x:" "\:x}["the letters in the array are in a different case";enlist "In"]
"the letters in the array are a different case"

q)k){" "/:x_/y@>y:,/1_'&:'(_y)~/:\:_x:" "\:x}["5 times 5 is 25";(1#"5";1#"6")]
"5 times is 25"

Explicação:

Divida em espaço em branco, minúsculas as duas entradas, procure correspondências, remova tudo, exceto a primeira ocorrência, junte a sequência novamente.

{" "/:x_/y@>y:,/1_'&:'(_y)~/:\:_x:" "\:x} / the solution
{                                       } / lambda with implicit x & y args
                                  " "\:x  / split (\:) on whitespace " "
                                x:        / save result as x
                               _          / lowercase x
                          ~/:\:           / match (~) each right (/:), each left (\:)
                      (_y)                / lowercase y
                   &:'                    / where (&:) each ('), ie indices of matches
                1_'                       / drop first of each result
              ,/                          / flatten
            y:                            / save result as y
         y@>                              / descending indices (>) apply (@) to y
      x_/                                 / drop (_) from x
 " "/:                                    / join (/:) on whitespace " "
rua
fonte
1

JavaScript (Node.js) , 75 bytes

f=(s,a)=>a.map(x=>s=s.replace(eval(`/\\b${x}\\b */ig`),s=>i++?"":s,i=0))&&s

Experimente online!

DanielIndie
fonte
1
Como essa não é uma função recursiva, você não precisa incluir o f=na sua contagem de bytes. Você também pode salvar um byte, alterando os parâmetros, substituindo (s,a)=>por s=>a=>e chamando a função por f(s)(a).
Shaggy
@ Shaggy sim, mas eu realmente me importo com o golfe a definição da função, porque o negócio principal é o golfe do corpo. mas thx isso é uma boa dica :) #
317 DanielIndie
1

JavaScript ES6, 78 bytes

f=(s,a,t={})=>s.split` `.filter(w=>a.find(e=>w==e)?(t[w]?0:t[w]=1):1).join` `

Como funciona:

f=(s,a,t={})=> // Function declaration; t is an empty object by default
s.split` ` // Split the string into an array of words
.filter(w=> // Declare a function that, if it returns false, will delete the word
  a.find(e=>w==e) // Returns undeclared (false) if the word isn't in the list
  ?(t[w]?0 // If it is in the list and t[w] exists, return 0 (false)
    :t[w]=1) // Else make t[w] exist and return 1 (true)
  :1) // If the word isn't in the array, return true (keep the word for sure)
.join` ` // Rejoin the string
Ian
fonte
2
Bem-vindo ao PPCG! Como você não está usando o nome da função fpara uma chamada recursiva, uma função sem nome também seria um envio válido; portanto, você pode salvar dois bytes soltando o f=.
Martin Ender
Bem-vindo ao PPCG! Infelizmente, isso falha quando casos diferentes estão envolvidos.
Shaggy
Se não fosse por isso, você pode obter esse baixo para 67 bytes
Shaggy
@MartinEnder Obrigado pela dica!
Ian
@ Shaggy usando a matriz de entrada como objeto é uma ideia interessante em que eu não tinha pensado. Vou tentar corrigir o problema do caso.
Ian
0

PowerShell v3 ou posterior, 104 bytes

Param($s,$w)$w|?{$_-and$s-match($r="\b$_(?: |$)")}|%{$h,$t=$s-split$r;$s="$h$($Matches.0)$(-join$t)"};$s

Com o custo de um byte, ele pode ser executado no PS 2.0 substituindo $Matches.0por $Matches[0].

Versão longa:

Param($s, $w)
$w | Where-Object {$_ -and $s -match ($r = "\b$_(?: |$)")} |    # Process each word in the word list, but only if it matches the RegEx (which will be saved in $r).
    ForEach-Object {                                            # \b - word boundary, followed by the word $_, and either a space or the end of the string ($)
        $h, $t = $s -split $r                                   # Split the string on all occurrences of the word; the first substring will end up in $h(ead), the rest in $t(ail) (might be an array)
        $s = "$h$($Matches.0)$(-join $t)"                       # Create a string from the head, the first match (can't use the word, because of the case), and the joined tail array
    }
$s                                                              # Return the result

Uso
Salve como Whatever.ps1 e chame com a sequência e as palavras como argumentos. Se mais de uma palavra precisar ser passada, as palavras deverão ser agrupadas em @ ():

.\Whatever.ps1 -s "A cat called matt sat on a mat and wore a hat A cat called matt sat on a mat and wore a hat" -w @("cat", "mat")

Alternativa sem arquivo (pode ser colada diretamente em um console PS):
salve o script como ScriptBlock (dentro de chaves) em uma variável e chame seu método Invoke () ou use-o com Invoke-Command:

$f={Param($s,$w)$w|?{$_-and$s-match($r="\b$_(?: |$)")}|%{$h,$t=$s-split$r;$s="$h$($Matches.0)$(-join$t)"};$s}
$f.Invoke("A cat called matt sat on a mat and wore a hat A cat called matt sat on a mat and wore a hat", @("cat", "mat"))
Invoke-Command -ScriptBlock $f -ArgumentList "A cat called matt sat on a mat and wore a hat A cat called matt sat on a mat and wore a hat", @("cat", "mat")
user314159
fonte
0

Javascript, 150 bytes

s=(x, y)=>{let z=new Array(y.length).fill(0);let w=[];for(f of x)(y.includes(f))?(!z[y.indexOf(f)])&&(z[y.indexOf(f)]=1,w.push(f)):w.push(f);return w}
aimorris
fonte
Além dos problemas com o golfe (dê uma olhada nas outras soluções JS para obter algumas dicas), isso recebe a primeira entrada como uma matriz de palavras e gera uma matriz de palavras que não é permitida pela especificação do desafio. Também falha quando casos diferentes estão envolvidos.
Salsicha
@Shaggy "A saída pode ser o valor de retorno de uma função" Parece que retorna um valor da função?
aimorris
0

Limpo , 153 142 138 134 bytes

import StdEnv,StdLib,Text
@ =toUpperCase
$s w#s=split" "s
=join" "[u\\u<-s&j<-[0..]|and[i<>j\\e<-w,i<-drop 1(elemIndices(@e)(map@s))]]

Experimente online!

Define a função $ :: String [String] -> String, literalmente fazendo o que o desafio descreve. Ele localiza e remove todas as ocorrências após a primeira, para cada palavra de destino.

Furioso
fonte
0

Retina, 46 37 bytes

+i`(^|,)((.+),.*\3.* )\3( |$)
$2
.*,

-14 bytes graças a @Neil e +5 bytes por uma correção de bug.

Entrada no formato word1,word2,word3,sentence, porque não tenho certeza de como ter entrada em várias linhas (onde as entradas são usadas de maneira diferente) ..

Explicação:

Experimente online.

+i`(^|,)((.+),.*\3.* )\3( |$)   Main regex to match:
+i`                              Enable case insensitivity
   (^|,)                          Either the start of the string, or a comma
        (                         Open capture group 2
         (                         Open capture group 3
          .+                        1 or more characters
            )                      Close capture group 3
             ,                     A comma
              .*                   0 or more characters
                \3                 The match of capture group 3
                  .*               0 or more characters, followed by a space
                     )            Close capture group 2
                      \3          The match of capture group 2 again
                        ( |$)     Followed by either a space, or it's the end of the string
$2                              And replace everything with:
                                 The match of capture group 2

.*,                             Then get everything before the last comma (the list)
                                 and remove it (including the comma itself)
Kevin Cruijssen
fonte
1
Conforme escrito, você pode simplificar a primeira linha +i`((.+),.*\2.* )\2( |$)e a segunda, $1mas noto que seu código falha de often,he intended to keep ten geesequalquer maneira.
Neil
@ Neil Obrigado pelo golfe -14, e o bug foi corrigido com +1.
Kevin Cruijssen 22/02
... Só que isso agora falhar em um dos casos de teste originais ...
Neil
@ Neil Ah oops .. Corrigido novamente por +4 bytes.
Kevin Cruijssen 22/02
Bem, a boa notícia é que eu acho que você pode usar \bem vez de (^|,), mas a má notícia é que eu acho que você precisa \b\3\b(não criaram um caso de teste adequado ainda embora).
Neil
0

Vermelho , 98 bytes

func[s w][foreach v w[parse s[thru[any" "v ahead" "]any[to remove[" "v ahead[" "| end]]| skip]]]s]

Experimente online!

f: func [s w][ 
    foreach v w [                   ; for each string in the array
        parse s [                   ; parse the input string as follows:
            thru [                  ; keep everything thru: 
                any " "             ; 0 or more spaces followed by
                v                   ; the current string from the array followed by
                ahead " "           ; look ahead for a space
            ]
            any [ to remove [       ; 0 or more: keep to here; then remove: 
                " "                 ; a space followed by 
                v                   ; the current string from the array
                ahead [" " | end]]  ; look ahead for a space or the end of the string
            | skip                  ; or advance the input by one 
            ]
        ]
    ]
    s                               ; return the processed string 
]
Galen Ivanov
fonte
0

Casca , 13 bytes

wüöVËm_Ṗ3+⁰ew

Leva uma lista de seqüências de caracteres e uma única sequência como argumentos, nesta ordem. Supõe que a lista esteja livre de duplicatas. Experimente online!

Explicação

wüöVËm_Ṗ3+⁰ew  Inputs: list of strings L (explicit, accessed with ⁰), string S (implicit).
               For example, L = ["CASE","for"], s = "Case for a different case".
            w  Split S on spaces: ["Case","for","a","different","case"]
 ü             Remove duplicates wrt an equality predicate.
               This means that a function is called on each pair of strings,
               and if it returns a truthy value, the second one is removed.
  öVËm_Ṗ3+⁰e    The predicate. Arguments are two strings, say A = "Case", B = "case".
           e    Put A and B into a list: ["Case","case"]
         +⁰     Concatenate with L: ["CASE","for","Case","case"]
       Ṗ3       All 3-element subsets: [["CASE","for","Case"],["CASE","for","case"],
                                        ["CASE","Case","case"],["for","Case","case"]]
  öV            Does any of them satisfy this:
    Ë            All strings are equal
     m_          after converting each character to lowercase.
                In this case, ["CASE","Case","case"] satisfies the condition.
               Result: ["Case","for","a","different"]
w              Join with spaces, print implicitly.
Zgarb
fonte
0

Mínimo , 125 bytes

=a () =b a 1 get =c a 0 get " " split
(:d (b d in?) ((c d in?) (d b append #b) unless) (d b append #b) if) foreach
b " " join

A entrada está quotna pilha, com a String de entrada como primeiro elemento e uma quotdas seqüências duplicadas como segundo elemento, ou seja,

("this sentence has no matches" ("ten" "cheese"))
Panda0nEarth
fonte
0

Python 3 , 168 bytes

def f(s,W):
 s=s.split(" ");c={w:0for w in W}
 for w in W: 
  for i,v in enumerate(s):
   if v.lower()==w.lower():
    c[w]+=1
    if c[w]>1:s.pop(i)
 return" ".join(s)

Experimente online!

Trelzevir
fonte
0

AWK , 120 bytes

NR%2{for(;r++<NF;)R[tolower($r)]=1}NR%2==0{for(;i++<NF;$i=$(i+s))while(R[x=tolower($(i+s))])U[x]++?++s:i++;NF-=s}NR%2==0

Experimente online!

A parte "remover espaço em branco" tornou isso um pouco mais desafiador do que eu pensava. Definir um campo para ""remove um campo, mas deixa um separador extra.

O link TIO possui 28 bytes extras para permitir várias entradas.

A entrada é fornecida em 2 linhas. A primeira linha é a lista de palavras e a segunda é a "sentença". Observe que "palavra" e "palavra" não são consideradas idênticas à pontuação anexada. Ter requisitos de pontuação provavelmente tornaria esse um problema ainda mais divertido .

Robert Benson
fonte
0

Ruby , 63 61 60 59 bytes

->s,w{w.any?{|i|s.sub! /\b(#{i}\b.*) #{i}\b/i,'\1'}?redo:s}

Experimente online!

Uma versão mais curta que diferencia maiúsculas de minúsculas e falha ~ a cada 10 15 vezes devido à aleatoriedade (37 bytes)

->s,w{s.uniq{|i|w.member?(i)?i:rand}}
Asone Tuhid
fonte
0

Python 2 , 140 bytes

from re import*
p='\s?%s'
S,A=input()
for a in A:S=sub(p%a,lambda s:s.end()==search(p%a,S,flags=I).end()and s.group()or'',S,flags=I)
print S

Experimente online!

Explicação:

re.sub(..)pode tomar como argumento uma função em vez de uma string de substituição. Então aqui temos alguns lambda chiques. A função é chamada para cada ocorrência de padrão e um objeto é passado para essa função - matchobject. Este objeto possui informações sobre ocorrência fundada. Estou interessado no índice dessa ocorrência, que pode ser recuperado por start()ou end()função. O último é mais curto, portanto é usado.

Para excluir a substituição da primeira ocorrência de palavra, usei outra função de pesquisa de expressão regular para obter exatamente um primeiro e, em seguida, comparar índices, usando o mesmo end()

O sinalizador re.Ié uma versão curta dore.IGNORECASES

Gambá morto
fonte