Supprimer les doublons d'une chaîne

17

Inspiré par cette question modeste de StackOverflow .

L'idée est simple; étant donné une chaîne et un tableau de chaînes, supprimez toutes les instances de mots du tableau (en ignorant la casse) de la chaîne d'entrée autre que la première, ainsi que tout espace supplémentaire que cela peut laisser. Les mots doivent correspondre à des mots entiers dans la chaîne d'entrée et non à des parties de mots.

Par exemple, "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"]devrait sortir"A cat called matt sat on a mat and wore a hat A called matt sat on a and wore a hat"

Contribution

  • L'entrée peut être considérée comme une chaîne et un tableau de chaînes ou un tableau de chaînes où la chaîne d'entrée est le premier élément. Ces paramètres peuvent être dans l'un ou l'autre ordre.
  • La chaîne d'entrée ne peut pas être considérée comme une liste de chaînes délimitées par des espaces.
  • La chaîne d'entrée n'aura pas d'espaces de début, de fin ou consécutifs.
  • Toutes les entrées ne contiendront que des caractères [A-Za-z0-9] à l'exception de la chaîne d'entrée comprenant également des espaces.
  • Le tableau d'entrée peut être vide ou contenir des mots ne figurant pas dans la chaîne d'entrée.

Production

  • La sortie peut être soit la valeur de retour d'une fonction, soit imprimée sur STDOUT
  • La sortie doit être dans le même cas que la chaîne d'origine

Cas de test

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

Comme c'est le golf de code, le nombre d'octets le plus bas gagne!

Luke Stevens
la source

Réponses:

9

R , 84 octets

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

Essayez-le en ligne!

Moins de 100 octets sur un défi de qui n'est pas aussi ?

Explication:

Après avoir divisé la chaîne en mots, nous devons exclure ceux qui sont

  1. doublons et
  2. dans w

ou alternativement, en tournant cela sur sa tête, en gardant ceux qui sont

  1. la première occurrence d'un mot OU
  2. pas dedans w.

duplicatedrenvoie soigneusement les indices logiques de ceux qui ne sont pas la première occurrence, donc !duplicated()renvoie les indices de ceux qui sont les premières occurrences, et x%in%wrenvoie les indices logiques xde ceux qui sont dans w. Soigné.

Giuseppe
la source
6

Java 8, 117 110 octets

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

Explication:

Essayez-le en ligne.

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

Explication supplémentaire pour l'expression régulière:

(?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
la source
4

MATL , 19 18 octets

"Ybtk@kmFyfX<(~)Zc

Les entrées sont: un tableau de cellules de chaînes, puis une chaîne.

Essayez-le en ligne! Ou vérifiez tous les cas de test .

Comment ça fonctionne

"        % 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
la source
3

Perl 5 , 49 octets

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

Essayez-le en ligne!

9 (!!) octets enregistrés grâce à @TonHospel !

Dom Hastings
la source
1
Cela semble échouer pour This is a test Will this be correct Both will be removed+ this will. Les deux deuxièmes mots sont correctement supprimés, mais il a également supprimé bele second willpour une raison quelconque.
Kevin Cruijssen
1
@KevinCruijssen Hmmm, je peux voir pourquoi cela se produit en ce moment. J'essaierai de bien regarder demain midi, mais j'ai fixé pour l'instant au coût de +4. Merci de me le faire savoir!
Dom Hastings
Pour 49:@B=<>;$_=join$",grep!(/^$_$/xi~~@B&&$v{+lc}++),@F
Ton Hospel
@TonHospel Ahh, a passé un moment à essayer d' lcêtre appelé sans parens. Impressionnant! Et utiliser une expression rationnelle contre le tableau est beaucoup mieux, merci! J'ai du mal à me souvenir de tous vos conseils!
Dom Hastings
2

Pyth, 27 octets

jdeMf!}r0eT@mr0dQmr0dPT._cz

Essayez-le en ligne

Explication

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.

Je suis sûr que les 10 octets pour la vérification insensible à la casse peuvent être réduits, mais je ne vois pas comment.


la source
2

Stax , 21 octets CP437

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

25 octets une fois déballés,

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

Le résultat est un tableau. La sortie pratique pour Stax est d'un élément par ligne.

Exécutez et déboguez en ligne!

Explication

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
la source
2

Perl 6 , 49 octets

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

Essaye-le

Étendu:

->              # 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
la source
2

Perl 5 , 50 48 octets

Comprend +1pour-p

Donnez la chaîne cible suivie de chaque mot de filtre sur des lignes distinctes sur 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

Le chopn'est nécessaire que pour corriger l'espace de fin au cas où le dernier mot serait supprimé

Juste le code:

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

Essayez-le en ligne!

Ton Hospel
la source
1

JavaScript (ES6), 98 octets

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

K4 , 41 octets

Solution:

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

Exemples:

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"

Explication:

Séparer sur les espaces, mettre les deux entrées en minuscules, rechercher des correspondances, supprimer toutes les occurrences sauf la première, réunir à nouveau la chaîne.

{" "/: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 " "
streetster
la source
1

JavaScript (Node.js) , 75 octets

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

Essayez-le en ligne!

DanielIndie
la source
1
Comme il ne s'agit pas d'une fonction récursive, vous n'avez pas besoin d'inclure le f=dans votre nombre d'octets. Vous pouvez également enregistrer un octet en curryant les paramètres, en les remplaçant (s,a)=>par s=>a=>puis en appelant la fonction avec f(s)(a).
Shaggy
@Shaggy oui, mais ça me dérange vraiment de jouer à la définition de la fonction parce que le principal problème est de jouer au corps. mais thx c'est une bonne astuce :)
DanielIndie
1

JavaScript ES6, 78 octets

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

Comment ça fonctionne:

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
la source
2
Bienvenue chez PPCG! Puisque vous n'utilisez pas le nom fde la fonction pour un appel récursif, une fonction sans nom serait également une soumission valide, vous pouvez donc économiser deux octets en supprimant le f=.
Martin Ender
Bienvenue chez PPCG! Malheureusement, cela échoue lorsque différents cas sont impliqués.
Shaggy
Si ce n'était pas pour cela, vous pourriez le réduire à 67 octets
Shaggy
@MartinEnder Merci pour le conseil!
Ian
@Shaggy utilisant le tableau d'entrée comme objet est une idée intéressante à laquelle je n'avais pas pensé. Je vais essayer de résoudre le problème du cas.
Ian
0

PowerShell v3 ou version ultérieure, 104 octets

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

Au prix d'un octet, il peut fonctionner dans PS 2.0 en le remplaçant $Matches.0par $Matches[0].

Version longue:

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

Utilisation
Enregistrez sous Wwhat.ps1 et appelez avec la chaîne et les mots comme arguments. Si plusieurs mots doivent être passés, les mots doivent être enveloppés dans @ ():

.\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")

Alternative sans fichier (peut être collé directement dans une console PS):
enregistrez le script sous ScriptBlock (entre accolades) dans une variable, puis appelez sa méthode Invoke (), ou utilisez-la avec 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
la source
0

Javascript, 150 octets

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
la source
Outre les problèmes avec le golf (jetez un œil aux autres solutions JS pour quelques conseils), cela prend la première entrée sous forme de tableau de mots et génère un tableau de mots qui n'est pas autorisé par la spécification de défi. Il échoue également lorsque différents cas sont impliqués.
Shaggy
@Shaggy "La sortie peut être soit la valeur de retour d'une fonction" On dirait qu'elle renvoie une valeur de la fonction?
aimorris
0

Propre , 153 142 138 134 octets

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

Essayez-le en ligne!

Définit la fonction $ :: String [String] -> String, faisant littéralement ce que le défi décrit. Il recherche et supprime chaque occurrence après la première, pour chaque mot cible.

Οurous
la source
0

Rétine, 46 37 octets

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

-14 octets grâce à @Neil , et +5 octets pour une correction de bogue.

Entrez dans le format word1,word2,word3,sentence, car je ne sais pas comment avoir une entrée multi-lignes (où les entrées sont utilisées différemment) ..

Explication:

Essayez-le en ligne.

+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
la source
1
Comme écrit, vous pouvez simplifier la première ligne +i`((.+),.*\2.* )\2( |$)et la seconde, $1mais je remarque que votre code échoue de often,he intended to keep ten geesetoute façon.
Neil
@Neil Merci pour le golf -14 et correction du bug avec +1.
Kevin Cruijssen
... sauf que cela échoue maintenant sur l'un des cas de test d'origine ...
Neil
@Neil Ah oups .. Corrigé à nouveau pour +4 octets.
Kevin Cruijssen
Eh bien, la bonne nouvelle est que je pense que vous pouvez utiliser à la \bplace de (^|,), mais la mauvaise nouvelle est que je pense que vous en avez besoin \b\3\b(je n'ai pas encore conçu de cas de test approprié).
Neil
0

Rouge , 98 octets

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

Essayez-le en ligne!

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
la source
0

Décortiquer , 13 octets

wüöVËm_Ṗ3+⁰ew

Prend une liste de chaînes et une seule chaîne comme arguments, dans cet ordre. Suppose que la liste est exempte de doublons. Essayez-le en ligne!

Explication

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
la source
0

Min , 125 octets

=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

L'entrée est quotsur la pile avec la chaîne d'entrée comme premier élément et une quotdes chaînes en double comme deuxième élément, c'est-à-dire

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

Python 3 , 168 octets

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)

Essayez-le en ligne!

Trelzevir
la source
0

AWK , 120 octets

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

Essayez-le en ligne!

La partie "supprimer les espaces blancs" a rendu cela un peu plus difficile que je ne le pensais. Définition d'un champ sur"" supprime un champ, mais laisse un séparateur supplémentaire.

Le lien TIO a 28 octets supplémentaires pour permettre plusieurs entrées.

L'entrée est donnée sur 2 lignes. La première ligne est la liste des mots et la seconde est la "phrase". Notez que «mot» et «mot» ne sont pas considérés comme identiques à la ponctuation jointe. Avoir des exigences de ponctuation rendrait probablement ce problème encore plus amusant .

Robert Benson
la source
0

Rubis , 63 61 60 59 octets

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

Essayez-le en ligne!

Une version plus courte qui respecte la casse et échoue ~ tous les 10 15 fois en raison du caractère aléatoire (37 octets)

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

Python 2 , 140 octets

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

Essayez-le en ligne!

Explication:

re.sub(..)peut prendre comme argument une fonction au lieu d'une chaîne de remplacement. Nous avons donc ici un lambda de fantaisie. Une fonction est appelée pour chaque occurrence de motif et un objet est transmis à cette fonction - matchobject. Cet objet contient des informations sur l'occurrence fondée. Je suis intéressé par l'index de cette occurrence, qui peut être récupéré parstart() ou end()fonction. Ce dernier est plus court, il est donc utilisé.

Pour exclure le remplacement de la première occurrence du mot, j'ai utilisé une autre fonction de recherche d'expression régulière pour en obtenir exactement un puis comparer les index, en utilisant le même end()

Le drapeau re.Iest une version courte dere.IGNORECASES

Possum mort
la source