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
  • 6,261
  • 9
  • 39
  • 77

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
  • 203,151
  • 43
  • 392
  • 509
  • 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
  • 70,012
  • 31
  • 140
  • 215
  • It is definitely debatable and probably depends on the Python experience of the other people reading the code. But your point is well taken. – ely Oct 11 '13 at 14:26
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
  • 10,353
  • 5
  • 31
  • 39