What is the elegant way in python for this kind of case ?
products_names = ''
for movement in movements:
for product in movement.products:
products_names += product.name + ', '
This will work fine:
products_names = ', '.join([product.name for movement in movements for product in movement.products])
The list comprehension, creates a list of product.name
for all the products. And then we join
the list on ', '
to get the final string.
You can also avoid creating the list alltogether, by using a generator expression:
products_names = ', '.join(product.name for movement in movements for product in movement.products)
Note that, with join()
, list comprehension gives better result than generator expression.
product_names = ", ".join([product.name for movement in movements for product in movement.products])
This solution takes advantage of the join
method of a string as well as a list comprehension.
Note also that I used a comprehension rather than a generator expression. This is because, while generators are usually faster than comprehensions, the join
method actually works better with a comprehension. See this reference.
Using map
and an anonymous function may make it more readable in this case; it's debatable.
", ".join(map(lambda x: ", ".join(x.products), movements))
reduce
could also be used but is probably less readable than the comprehensions:
reduce(lambda y,z: y + ", " + z, map(lambda x: ", ".join(x.products), movements))
You can shorten some of the above examples slightly by using chain
, which is great for flattening lists of lists:
from itertools import chain
product_names = ', '.join([x.name for x in chain(*movements)])
Assuming that movements
== [l1, l2, l3, ...]
and for each l
, they look like [p1, p2, p3, p4, ...]
, then this is what it does:
chain(*movements)
This does one level of flattening on movement. The * rolls out movements into the chain function - i.e. this is effectively the same as calling chain(l1, l2, l3)
.
[x.name for x in chain(*movements)]
But we actually want some processing on each item, so add a list comprehension to pull the value out we want instead.
note that the list comprehension is still faster, because of join. without doing lookups, i got the following results:
tl = [['a', 'b', 'c'], ['c', 'd', 'e'], ['e', 'f', 'g']]
%timeit ','.join(chain(*tl))
100000 loops, best of 3: 1.78 us per loop
%timeit ','.join([x for l in tl for x in l])
1000000 loops, best of 3: 1.09 us per loop
%timeit ','.join([x for x in chain(*tl)])
1000000 loops, best of 3: 1.41 us per loop