3

Say I have a string that looks like

'one, two, three,'

What is a pythonic way of iterating through replacing ',' with '.' one at a time? Ideally the return value of the function would look like:

['one. two, three,' , 'one, two. three,' , 'one, two, three.']

Reasoning for selected answer, thanks for contributing!

import timeit

def one(s):
    b = ["%s.%s" % (s[:i], s[i+1:]) for i, c in enumerate(s) if c == ","]

def two(s):
    b = [s[:i] + "." + s[i+1:] for i in range(len(s)) if s[i] == ","]

def thr(s):
    b = [s[:i] + "." + s[i+1:] for i, c in enumerate(s) if s[i] == ","]

def fou(s):
    ss = s.split(',')
    b = [','.join(ss[:i]) + '.' + ','.join(ss[i:]) for i in range(1,len(ss))]

a = 'one,two,three,four,five,six,seven,eight,nine,ten,eleven,twelve,'

print(timeit.timeit('one(a)', 'from __main__ import one, a', number = 1000000))
print(timeit.timeit('two(a)', 'from __main__ import two, a', number = 1000000))
print(timeit.timeit('thr(a)', 'from __main__ import thr, a', number = 1000000))
print(timeit.timeit('fou(a)', 'from __main__ import fou, a', number = 1000000))

#   C:\dev\goddangit>python timin.py
#   14.3008527857
#   11.8759967856
#   13.3739626708
#   18.8536401851
gzzo
  • 241
  • 5
  • 12
  • 3
    Smells like homework. – Ignacio Vazquez-Abrams May 22 '13 at 07:10
  • 2
    @IgnacioVazquez-Abrams: not sure how that helps, this is a small function of a much larger project that currently takes up 15 lines that I feel could be reduced to just a couple (hence the request for a pythonic answer). I could post two working versions of this if you are interested. – gzzo May 22 '13 at 07:20

4 Answers4

5

One-liner, s being 'one, two, three,':

>>> [s[:i] + "." + s[i+1:] for i in range(len(s)) if s[i] == ","]
['one. two, three,', 'one, two. three,', 'one, two, three.']

Alternatively, replace the outermost [ ] by ( ) to have a generator object instead.

That is, of course, only for single-character replacements. For more generally replacing substrings with other strings you should use one of the other solutions, e.g., using regular expressions.

tobias_k
  • 81,265
  • 12
  • 120
  • 179
  • iterator is general concept: any object whose class has a next method and an __iter__ method that does return self. http://stackoverflow.com/questions/2776829/difference-between-python-generators-vs-iterators – luke14free May 22 '13 at 07:31
  • You can easily change the square brackets in the list comp `[...]`, to `(...)` and it becomes a generator expression – jamylak May 22 '13 at 07:32
  • 1
    i really don't like the part where you enumerate and test all characters in the string. a regex or a find seems more appropriate. – njzk2 May 22 '13 at 07:45
  • @njzk2 Wouldn't regex or find essentially do the same thing, internally? – tobias_k May 22 '13 at 08:10
  • @tobias_k: the advantage of the regex answer is that it works with arbitrary search strings: for example, turn `A...B...C...etc` into `[A*B...C..., A...B*C... etc]`. – georg May 22 '13 at 08:57
  • yes, but that would be internally. The code would appear more functionnal and display less low-lewel plumbing. – njzk2 May 22 '13 at 09:00
  • @thg435 Of course, thanks for pointing this out! For single-character replacements, I still find this to be the most readable way, though. ;-) – tobias_k May 22 '13 at 09:35
4

You can use enumerate:

 ["%s.%s" % (s[:i], s[i+1:]) for i, c in enumerate(s) if c == ","]

or regular expressions:

 ["%s.%s" % (s[:m.start()], s[m.start()+1:]) for m in re.finditer(',', s)]
Thomas Jung
  • 32,428
  • 9
  • 84
  • 114
4

A simple as I can find :

import re
s = 'one, two, three,'
# delim will be used as a regex
delim = ','
[s[:i.start()] + "." + s[i.end():] for i in re.finditer(delim, s)]
njzk2
  • 38,969
  • 7
  • 69
  • 107
  • 1
    Good stuff, although I'd use `i.end()` in the second one to make it work for arbitrary strings. – georg May 22 '13 at 07:57
2
import itertools

l_string = "one, two, three,".rstrip(',').split(',')
separators = lambda pos: [ '.' if i==pos else ',' for i,x in enumerate(l_string) ]
print [ "".join([ "".join(elem) for elem in itertools.izip( l_string, separators(pos)  ) ]) for pos in range(len(l_string)) ]

>>> ['one. two, three,', 'one, two. three,', 'one, two, three.']
Inbar Rose
  • 41,843
  • 24
  • 85
  • 131
lucasg
  • 10,734
  • 4
  • 35
  • 57