269

I have a set of strings and all the strings have one of two specific substrings which I want to remove:

set1 = {'Apple.good', 'Orange.good', 'Pear.bad', 'Pear.good', 'Banana.bad', 'Potato.bad'}

I want the ".good" and ".bad" substrings removed from all the strings. I tried this:

for x in set1:
    x.replace('.good', '')
    x.replace('.bad', '')

but it doesn't seem to work, set1 stays exactly the same. I tried using for x in list(set1) instead but that doesn't change anything.

Boris Verkhovskiy
  • 14,854
  • 11
  • 100
  • 103
controlfreak
  • 3,165
  • 3
  • 12
  • 14
  • I have plans to replace the canonical I have been using, https://stackoverflow.com/questions/25082410/apply-function-to-each-element-of-a-list, with a better, *more general* duplicate. The logic works the same way whether we call functions on the elements, call methods on the elements or use a simple expression to describe the new value; and it works essentially the same way if we iterate over any kind of iterable (and we can create multiple different kinds of results, depending on what kind of comprehension is used). – Karl Knechtel Oct 03 '22 at 21:29
  • After several months, nothing better surfaced, so I implemented the aforementioned plan today. – Karl Knechtel Mar 07 '23 at 20:09

9 Answers9

312

Strings are immutable. str.replace creates a new string. This is stated in the documentation:

str.replace(old, new[, count])

Return a copy of the string with all occurrences of substring old replaced by new. [...]

This means you have to re-allocate the set or re-populate it (re-allocating is easier with a set comprehension):

new_set = {x.replace('.good', '').replace('.bad', '') for x in set1}

P.S. if you want to change the prefix or suffix of a string and you're using Python 3.9 or newer, use str.removeprefix() or str.removesuffix() instead:

new_set = {x.removesuffix('.good').removesuffix('.bad') for x in set1}
Boris Verkhovskiy
  • 14,854
  • 11
  • 100
  • 103
Reut Sharabani
  • 30,449
  • 6
  • 70
  • 88
  • 3
    Note: It also works with a list, simply use `[...]` instead of `{...}` – Mo711 Apr 25 '21 at 14:56
  • 2
    What's the point of editing this question to update it with `str.removesuffix()` for Python 3.9+ by copying the answer by @DineshKumar? It is fairer to let people credit the author of that answer. – divenex Jun 28 '22 at 08:59
  • 1
    @divenex I would edit back and link to dinesh's answer instead of copying it. I think its legit because it lives on the same page so its not like a link that can actually expire easily. – Reut Sharabani Jun 28 '22 at 18:25
  • Please stop adding information from other answers here. Edits are to improve an answer, not to change it from its original meaning. Let the newer, better answer bubble up by upvoting it. – Cris Luengo Nov 01 '22 at 20:43
112
>>> x = 'Pear.good'
>>> y = x.replace('.good','')
>>> y
'Pear'
>>> x
'Pear.good'

.replace doesn't change the string, it returns a copy of the string with the replacement. You can't change the string directly because strings are immutable.

You need to take the return values from x.replace and put them in a new set.

Alex Hall
  • 34,833
  • 5
  • 57
  • 89
20

In Python 3.9+ you could remove the suffix using str.removesuffix('mysuffix'). From the docs:

If the string ends with the suffix string and that suffix is not empty, return string[:-len(suffix)]. Otherwise, return a copy of the original string

So you can either create a new empty set and add each element without the suffix to it:

set1  = {'Apple.good', 'Orange.good', 'Pear.bad', 'Pear.good', 'Banana.bad', 'Potato.bad'}

set2 = set()
for s in set1:
   set2.add(s.removesuffix(".good").removesuffix(".bad"))

Or create the new set using a set comprehension:

set2 = {s.removesuffix(".good").removesuffix(".bad") for s in set1}
   
print(set2)

Output:

{'Orange', 'Pear', 'Apple', 'Banana', 'Potato'}
Boris Verkhovskiy
  • 14,854
  • 11
  • 100
  • 103
DineshKumar
  • 1,599
  • 2
  • 16
  • 30
17

All you need is a bit of black magic!

>>> a = ["cherry.bad","pear.good", "apple.good"]
>>> a = list(map(lambda x: x.replace('.good','').replace('.bad',''),a))
>>> a
['cherry', 'pear', 'apple']
gueeest
  • 195
  • 1
  • 2
10

When there are multiple substrings to remove, one simple and effective option is to use re.sub with a compiled pattern that involves joining all the substrings-to-remove using the regex OR (|) pipe.

import re

to_remove = ['.good', '.bad']
strings = ['Apple.good','Orange.good','Pear.bad']

p = re.compile('|'.join(map(re.escape, to_remove))) # escape to handle metachars
[p.sub('', s) for s in strings]
# ['Apple', 'Orange', 'Pear']
cs95
  • 379,657
  • 97
  • 704
  • 746
5

You could do this:

import re
import string
set1={'Apple.good','Orange.good','Pear.bad','Pear.good','Banana.bad','Potato.bad'}

for x in set1:
    x.replace('.good',' ')
    x.replace('.bad',' ')
    x = re.sub('\.good$', '', x)
    x = re.sub('\.bad$', '', x)
    print(x)
Vivek
  • 449
  • 18
  • 36
  • 4
    line `x.replace('.good',' ')` and `x.replace('.bad',' ')` does not do anything to the final result. The print out will be the same without them. – Srđan Popić Feb 16 '18 at 11:38
  • Also I would rather have just one line with `re.sub`, like this: `x = re.sub('((\.good$)|(\.bad$))', '', x)` – Srđan Popić Feb 16 '18 at 11:52
  • @SrđanPopić yeah I agree with you – Vivek Jan 21 '19 at 04:25
  • should we edit it accordingly? (remove `replace`s and move everything to one `re.sub` call) – Srđan Popić Jan 23 '19 at 08:15
  • 1
    @SrđanPopić I post this answer because it is simple and step wise. – Vivek Jan 23 '19 at 09:35
  • 1
    in Python strings a immutable. `str.replace(old, new)` returns a copy of the string with all occurrences of substring 'old' replaced by 'new'. Its result must be assigned to a new variable. Just like it was explained in the accepted answer: https://stackoverflow.com/a/37372690/2690353 – infinityzxx Jan 24 '20 at 22:01
5
# practices 2
str = "Amin Is A Good Programmer"
new_set = str.replace('Good', '')
print(new_set)

 

print : Amin Is A  Programmer
Amin
  • 342
  • 4
  • 15
  • 1
    Hi Amin. While this code may answer the question, providing additional context regarding why and/or how this code answers the question improves its long-term value. [How to Answer](https://stackoverflow.com/help/how-to-answer). Kind Regards. – Elletlar Nov 13 '20 at 11:26
  • Hi, my friend, this code works with other long text. so even you can to get your string from the input and find text point ... – Amin Nov 13 '20 at 12:10
  • It is very bad practice to rename builtins like `str`. – Chris Collett Apr 01 '21 at 17:32
  • that is not a set. – Boris Verkhovskiy Apr 15 '21 at 23:16
  • why that is not set but I know that is true. so what is your problem in this code. – Amin Apr 16 '21 at 01:02
  • I always try to choose the easiest ways but does this code have a problem? I saw it easily and shared it, but if there is a problem, tell me the reason. Sincerely, Amin. -Elletlar – Amin Jun 13 '22 at 11:57
4

I did the test (but it is not your example) and the data does not return them orderly or complete

>>> ind = ['p5','p1','p8','p4','p2','p8']
>>> newind = {x.replace('p','') for x in ind}
>>> newind
{'1', '2', '8', '5', '4'}

I proved that this works:

>>> ind = ['p5','p1','p8','p4','p2','p8']
>>> newind = [x.replace('p','') for x in ind]
>>> newind
['5', '1', '8', '4', '2', '8']

or

>>> newind = []
>>> ind = ['p5','p1','p8','p4','p2','p8']
>>> for x in ind:
...     newind.append(x.replace('p',''))
>>> newind
['5', '1', '8', '4', '2', '8']
user140259
  • 450
  • 2
  • 5
  • 16
0

If list

I was doing something for a list which is a set of strings and you want to remove all lines that have a certain substring you can do this

import re
def RemoveInList(sub,LinSplitUnOr):
    indices = [i for i, x in enumerate(LinSplitUnOr) if re.search(sub, x)]
    A = [i for j, i in enumerate(LinSplitUnOr) if j not in indices]
    return A

where sub is a patter that you do not wish to have in a list of lines LinSplitUnOr

for example

A=['Apple.good','Orange.good','Pear.bad','Pear.good','Banana.bad','Potato.bad']
sub = 'good'
A=RemoveInList(sub,A)

Then A will be

enter image description here

Community
  • 1
  • 1
rsc05
  • 3,626
  • 2
  • 36
  • 57