0

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 + ', '
bux
  • 7,087
  • 11
  • 45
  • 86

4 Answers4

6

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.

Rohit Jain
  • 209,639
  • 45
  • 409
  • 525
  • Note that a list comprehension is more efficient with the `join` method. –  Oct 11 '13 at 14:24
3
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.

Community
  • 1
  • 1
3

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))
ely
  • 74,674
  • 34
  • 147
  • 228
1

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
Corley Brigman
  • 11,633
  • 5
  • 33
  • 40