Eliminar duplicados de una cadena

17

Inspirado en esta sencilla pregunta de StackOverflow .

La idea es simple; dada una Cadena y una matriz de Cadenas, elimine cualquier instancia de palabras en la matriz (ignorando mayúsculas y minúsculas) de la Cadena de entrada que no sea la primera, junto con cualquier espacio en blanco adicional que esto pueda dejar. Las palabras deben coincidir con palabras completas en la cadena de entrada, y no con partes de palabras.

por ejemplo, "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"]debería salir"A cat called matt sat on a mat and wore a hat A called matt sat on a and wore a hat"

Entrada

  • La entrada puede tomarse como una Cadena y como una matriz de Cadenas o una matriz de Cadenas donde la Cadena de entrada es el primer elemento. Estos parámetros pueden estar en cualquier orden.
  • La cadena de entrada no puede tomarse como una lista de cadenas delimitadas por espacios.
  • La cadena de entrada no tendrá espacios iniciales, finales o consecutivos.
  • Toda entrada solo contendrá caracteres [A-Za-z0-9] con la excepción de la Cadena de entrada que también incluye espacios.
  • La matriz de entrada puede estar vacía o contener palabras que no están en la cadena de entrada.

Salida

  • La salida puede ser el valor de retorno de una función o imprimirse en STDOUT
  • La salida debe estar en el mismo caso que la cadena original

Casos de prueba

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 se trata de código de golf, gana el conteo de bytes más bajo

Luke Stevens
fuente

Respuestas:

9

R , 84 bytes

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

Pruébalo en línea!

¿Menos de 100 bytes en un desafío de que no es también ?

Explicación:

Después de dividir la cadena en palabras, debemos excluir aquellas que son

  1. duplicados y
  2. en w

o alternativamente, girando eso sobre su cabeza, manteniendo los que están

  1. La primera aparición de una palabra O
  2. no en w.

duplicateddevuelve índices lógicamente de aquellos que no son la primera aparición, por lo que !duplicated()devuelve índices de aquellos que son las primeras ocurrencias, y x%in%wdevuelve índices lógicos xde aquellos que están en w. Ordenado.

Giuseppe
fuente
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;}

Explicación:

Pruébalo en línea.

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

Explicación adicional para la expresión regular:

(?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
fuente
4

MATL , 19 18 bytes

"Ybtk@kmFyfX<(~)Zc

Las entradas son: un conjunto de celdas de cadenas, luego una cadena.

Pruébalo en línea! O verificar todos los casos de prueba .

Cómo 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
fuente
3

Perl 5 , 49 bytes

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

Pruébalo en línea!

¡ Ahorró 9 (!!) bytes gracias a @TonHospel !

Dom Hastings
fuente
1
Esto parece fallar para This is a test Will this be correct Both will be removed+ this will. Las dos últimas palabras se eliminan correctamente, pero también eliminó bela segunda después de la segunda willpor alguna razón.
Kevin Cruijssen
1
@KevinCruijssen Hmmm, puedo ver por qué está sucediendo en este momento. Trataré de echarle un buen vistazo al almuerzo mañana, pero lo he arreglado por ahora a un costo de +4. ¡Gracias por hacérmelo saber!
Dom Hastings
Por 49:@B=<>;$_=join$",grep!(/^$_$/xi~~@B&&$v{+lc}++),@F
Ton Hospel
@TonHospel Ahh, pasé un tiempo tratando de que me lcllamaran sin padres. ¡Increíble! Y usar una expresión regular contra la matriz es mucho mejor, ¡Gracias! ¡Me cuesta recordar todos tus consejos!
Dom Hastings
2

Pyth, 27 bytes

jdeMf!}r0eT@mr0dQmr0dPT._cz

Pruébalo en línea

Explicación

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.

Estoy seguro de que los 10 bytes para la comprobación de mayúsculas y minúsculas se pueden reducir, pero no veo cómo.


fuente
2

Stax , 21 bytes CP437

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

25 bytes cuando está desempaquetado,

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

El resultado es una matriz. La salida conveniente para Stax es un elemento por línea.

¡Ejecute y depure en línea!

Explicación

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

Perl 6 , 49 bytes

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

Pruébalo

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

Perl 5 , 50 48 bytes

Incluye +1para-p

Dé la cadena de destino seguida de cada palabra de filtro en líneas separadas en 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

los chop única que se necesita para arreglar el espacio final en caso de que la última palabra se retira

Solo el código:

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

Pruébalo en línea!

Ton Hospel
fuente
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` `
ETHproducciones
fuente
1

K4 , 41 bytes

Solución:

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

Ejemplos:

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"

Explicación:

Dividir en espacios en blanco, poner en minúsculas ambas entradas, buscar coincidencias, eliminar todas menos la primera aparición, volver a unir la cadena.

{" "/: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 " "
callejero
fuente
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

Pruébalo en línea!

DanielIndie
fuente
1
Como esta no es una función recursiva, no necesita incluirla f=en su recuento de bytes. También puede guardar un byte por ganarse los parámetros, reemplazando (s,a)=>con s=>a=>y después de llamar a la función con f(s)(a).
Shaggy
@ Shaggy sí, pero realmente me importa jugar al golf la definición de la función porque el trato principal es jugar golf en el cuerpo. pero eso es un buen consejo :)
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` `

Cómo 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
fuente
2
Bienvenido a PPCG! Como no está utilizando el nombre fde la función para una llamada recursiva, una función sin nombre también sería un envío válido, por lo que puede guardar dos bytes soltando el f=.
Martin Ender
Bienvenido a PPCG! Lamentablemente esto falla cuando hay diferentes casos involucrados.
Shaggy
Si no fuera por eso, podría reducirlo a 67 bytes
Shaggy
@ Martininder ¡Gracias por la sugerencia!
Ian
@Shaggy usando la matriz de entrada como objeto es una idea interesante que no había pensado. Intentaré solucionar el problema del caso.
Ian
0

PowerShell v3 o posterior, 104 bytes

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

Al costo de un byte, puede ejecutarse en PS 2.0 reemplazándolo $Matches.0por $Matches[0].

Versión larga:

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
Guardar como Whatever.ps1 y llamar con la cadena y las palabras como argumentos. Si se necesita pasar más de una palabra, las palabras deben estar envueltas en @ ():

.\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 sin archivo (se puede pegar directamente en una consola PS):
guarde el script como ScriptBlock (dentro de llaves) en una variable, luego llame a su método Invoke (), o úselo con 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")
usuario314159
fuente
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
fuente
Además de los problemas con el golf (eche un vistazo a las otras soluciones de JS para obtener algunos consejos allí), esto toma la primera entrada como un conjunto de palabras y genera un conjunto de palabras que no está permitido por la especificación de desafío. También falla cuando hay diferentes casos involucrados.
Shaggy
@Shaggy "La salida puede ser el valor de retorno de una función" ¿Parece que devuelve un valor de la función?
aimorris
0

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

Pruébalo en línea!

Define la función $ :: String [String] -> String, haciendo literalmente lo que describe el desafío. Encuentra y elimina cada aparición después de la primera, para cada palabra objetivo.

Οurous
fuente
0

Retina, 46 37 bytes

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

-14 bytes gracias a @Neil , y +5 bytes para una corrección de errores.

Entrada en el formato word1,word2,word3,sentence , porque no estoy seguro de cómo tener una entrada de varias líneas (donde las entradas se usan de manera diferente).

Explicación:

Pruébalo en línea.

+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
fuente
1
Tal como está escrito, puede simplificar la primera línea +i`((.+),.*\2.* )\2( |$)y la segunda, $1pero noto que su código falla de often,he intended to keep ten geesetodos modos.
Neil
@Neil Gracias por el golf -14, y solucionó el error con +1.
Kevin Cruijssen
... excepto que esto ahora falla en uno de los casos de prueba originales ...
Neil
@Neil Ah oops ... Se corrigió nuevamente por +4 bytes.
Kevin Cruijssen
Bueno, la buena noticia es que creo que puede usar en \blugar de (^|,), pero la mala noticia es que creo que lo necesita \b\3\b(aunque todavía no he ideado un caso de prueba adecuado).
Neil
0

Rojo , 98 bytes

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

Pruébalo en línea!

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

Casco , 13 bytes

wüöVËm_Ṗ3+⁰ew

Toma una lista de cadenas y una sola cadena como argumentos, en este orden. Asume que la lista está libre de duplicados. Pruébalo en línea!

Explicación

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

Min , 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

La entrada está quoten la pila con la cadena de entrada como primer elemento, y una quotde las cadenas duplicadas como segundo elemento, es decir

("this sentence has no matches" ("ten" "cheese"))
Panda0nEarth
fuente
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)

Pruébalo en línea!

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

Pruébalo en línea!

La parte "eliminar espacios en blanco" hizo que esto fuera un poco más desafiante de lo que pensaba. Establecer un campo para"" , se elimina un campo, pero se deja un separador adicional.

El enlace TIO tiene 28 bytes adicionales para permitir múltiples entradas.

La entrada se da en 2 líneas. La primera línea es la lista de palabras y la segunda es la "oración". Tenga en cuenta que "palabra" y "palabra" no se consideran idénticas a la puntuación adjunta. Tener requisitos de puntuación probablemente haría que este sea un problema aún más divertido .

Robert Benson
fuente
0

Ruby , 63 61 60 59 bytes

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

Pruébalo en línea!

Una versión más corta que distingue entre mayúsculas y minúsculas y falla ~ cada 10 15 veces debido a la aleatoriedad (37 bytes)

->s,w{s.uniq{|i|w.member?(i)?i:rand}}
Asone Tuhid
fuente
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

Pruébalo en línea!

Explicación:

re.sub(..)puede tomar como argumento una función en lugar de una cadena de reemplazo. Así que aquí tenemos una elegante lambda. Se llama a la función para cada aparición del patrón y se pasa un objeto a esta función: matchobject. Este objeto tiene información sobre la ocurrencia fundada. Estoy interesado en el índice de esta ocurrencia, que puede ser recuperado por start()oend() función. Este último es más corto, por lo que se usa.

Para excluir el reemplazo de la primera aparición de la palabra, utilicé otra función de búsqueda de expresiones regulares para obtener exactamente la primera y luego comparar índices, usando el mismo end()

Flag re.Ies una versión corta dere.IGNORECASES

Zarigüeya muerta
fuente