2

I am new to Python and noticed that the following.

>>> 'D'*1
'D'

Therefore i wonder whether we can do multiplication and join the string (for choice, element can be 0 or 1 only)

>>> print(choice)
[1, 1, 1, 1]
>>> print(A)
['D', 'e', '0', '}']

My desired output would be 'De0}'

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
useR
  • 3,062
  • 10
  • 51
  • 66
  • What would print if `choice=[1,1,1,2]`? – Ffisegydd Sep 12 '14 at 09:11
  • there is another issue. my scenrio is that i would like to let the user to have a checkbox. if the item is checked, then 1, 0 otherwise. Therefore only 0 or 1 in this case. – useR Sep 12 '14 at 09:12
  • @useR: in that case, you shouldn't really need to multiply here; just use 'compression'; pick elements or ignore them based on the truth value of the elements in `choice`. – Martijn Pieters Sep 12 '14 at 09:30
  • terribly sorry that i didnt state my problem clearly, as i would like to do random.shuffle for A first and then join the element. – useR Sep 12 '14 at 09:41
  • @useR: then do so? Or should `choice` be shuffled *along* with `A`? That's really a *separate* problem however. It doesn't matter if `A` is shuffled or not, because you should focus on the distinct problem you presented here: how to select items in one list based on the value of another. – Martijn Pieters Sep 12 '14 at 09:42

2 Answers2

7

You can explicity zip the lists, mulitply and then join the result:

''.join([c * i for c, i in zip(A, choice)])

The zip() function pairs up the characters from A with the integers from choice, the list comprehension then multiplies the character with the integer, and the str.join() call combines the result of that into one string.

If choice is used just used to select elements, you are better of using itertools.compress() here:

from itertools import compress

''.join(compress(A, choice))

compress() does exactly what you were intending to do: pick elements from the first iterable based on wether or not the corresponding value in the second iterable is true or false.

Demo:

>>> choice = [1, 1, 1, 1]
>>> A = ['D', 'e', '0', '}']
>>> ''.join([c * i for c, i in zip(A, choice)])
'De0}'
>>> choice = [0, 1, 0, 1]
>>> ''.join([c * i for c, i in zip(A, choice)])
'e}'
>>> from itertools import compress
>>> ''.join(compress(A, choice))
'e}'

Using itertools.compress() is the far faster option here:

>>> import timeit
>>> import random
>>> A = [chr(random.randrange(33, 127)) for _ in range(1000)]
>>> choice = [random.randrange(2) for _ in range(1000)]
>>> def with_mult(A, choice):
...     return ''.join([c * i for c, i in zip(A, choice)])
... 
>>> def with_compress(A, choice):
...     return ''.join(compress(A, choice))
... 
>>> timeit.timeit('f(A, choice)', 'from __main__ import A, choice, with_mult as f', number=10000)
1.0436905510141514
>>> timeit.timeit('f(A, choice)', 'from __main__ import A, choice, with_compress as f', number=10000)
0.2727453340048669

That's a 4x speed increase.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • And of course we can also do it with a generator expression: `''.join(c * i for c, i in zip('De0}', [1, 1, 1, 0]))` – PM 2Ring Sep 12 '14 at 09:23
  • @PM2Ring see http://stackoverflow.com/a/9061024/3005188 for why it's better to use a list comprehension as opposed to a generator expression when using `str.join`. – Ffisegydd Sep 12 '14 at 09:24
  • @PM2Ring: but a generator expression *with a `str.join()` call* is slower, because the `str.join()` has to have a list object to do two scans on (one to determine the total size, one to build the actual string). – Martijn Pieters Sep 12 '14 at 09:24
5

You can use a list comprehension with str.join and zip. See below for an example:

choice = [1, 1, 1, 0]
A = ['D', 'e', '0', '}']

out = ''.join([n*c for n, c in zip(choice, A)])

print(out)
# De0

As the 4th element of choice is 0 the 4th element of A ('}') is not printed.

Ffisegydd
  • 51,807
  • 15
  • 147
  • 125