0

I was a given a string s = "1_2_3_4" and I wanted to replace all "_" with another char - "0".

I used s = ''.join([c for c in s if c != '_']) to eliminate the "_" from my string, but I don't know how to replace the values. I wanted to do something like s = ''.join([c for c in s if c != '_' else '0']) but of course, that's invalid syntax.

I'm well aware that s.replace('_','0') will be a much better option, but I'm just trying to understand how can I use if statements inside a list comprehension. This will serve me for other cases when the class I'm using will not have replace method.

Tom Wyllie
  • 2,020
  • 13
  • 16
CIsForCookies
  • 12,097
  • 11
  • 59
  • 124

3 Answers3

4

Is this what you are looking for?

s = ''.join([c if c != '_' else '0' for c in s])

Btw another option would be:

s = '0'.join(s.split('_'))

I do not fully understand why not s.replace('_', '0') though.


Now as far as the "why" is concerned:

This is simply how the syntax of the language is. When the if comes before the for an else is also expected (an error is thrown if there isn't one). When the if comes after the for, an else following it is not allowed.

Ma0
  • 15,057
  • 4
  • 35
  • 65
  • Beat me to it by twelve seconds... ;) – Tom Wyllie Jul 11 '17 at 14:11
  • Why the else is legal before the loop but not after? And for the not using replace, I just want to understand better the if statements inside a list – CIsForCookies Jul 11 '17 at 14:13
  • @CIsForCookies , The left value is the appended value in each iteration, each iteration the left value is appended to the result list. If you do the if condition on the right, you will skip iterations that don't satisfy the if condition so you won't be able to modify the returned value. Therefore the if condition should be on the left side, as you do want to modify the returned value according to the value in each iteration. – omri_saadon Jul 11 '17 at 14:17
3

Try this generator expression with an in-line if else statement;

s = "1_2_3_4"
s = ''.join(((char if char != '_' else '0') for char in s))
print(s)

It's not a list comprehension, which is better in this use case. Still a one liner but is more efficient on larger strings.

Tom Wyllie
  • 2,020
  • 13
  • 16
  • Correct me if I'm wrong but generator efficiency here (in a 8 chars string) is nothing notable, isn't it? I find list easier to use, and more intuitive when I don't need to think of execution speed – CIsForCookies Jul 11 '17 at 14:17
  • 1
    @CIsForCookies Actually with the use of `join` `list`s are faster than `generators`. For this difference to be notable, bigger strings are needed though. – Ma0 Jul 11 '17 at 14:18
  • "is nothing notable" - you are completely correct, the performance enhancement is trivial. Probably not even measurable on this. I just think it's a good habit to get into. :) – Tom Wyllie Jul 11 '17 at 14:19
  • @Ev.Kounis is it because join expects list or tuple and if generator is provided it will has to create a list and only then use join? – CIsForCookies Jul 11 '17 at 14:21
  • 1
    @TomWyllie, ClsForCookies [See this comparison for details](https://stackoverflow.com/questions/11964130/list-comprehension-vs-generator-expressions-weird-timeit-results) – Ma0 Jul 11 '17 at 14:21
  • Actually @Ev.Kounis they possibly are, you make a valid point. I don't really know about the relative speeds. Generators are certainly far more memory efficient though. – Tom Wyllie Jul 11 '17 at 14:22
2

You can do this (it's better to use replace but you wished to use the join on a list):

s = "1_2_3_4"

s = ''.join([c if c != '_' else '0'  for c in s])

print (s)
>>> '1020304'
omri_saadon
  • 10,193
  • 7
  • 33
  • 58