views:

340

answers:

3

I have the following list comprehension which returns a list of coordinate objects for each location.

coordinate_list = [Coordinates(location.latitude, location.longitude)
                   for location in locations]

This works.

Now suppose the location object has a number_of_times member. I want a list comprehension to generate n Coordinate objects where n is the number_of_times for the particular location. So if a location has number_of_times = 5 then the coordinates for that location will be repeated 5 times in the list. (Maybe this is a case for a for-loop but I'm curious if it can be done via list comprehensions)

A: 

You can multiply a sequence by the number_of_times value. So [1,2]*3 will equal [1, 2, 1, 2, 1, 2] . If you get your coordinates in a list then multiply the list by the number of repeats, your result should be [coord, coord, coord].

def coordsFor(location):
   return coord = [Coordinates(location.latitude, location.longitude) ]*location.number_of_times

Concatenate the coordsFor of each element in the list.

reduce(operator.add, map(coordsFor, locations), [])
Ishpeck
@Ishpeck, he wants to repeat individual elements at different amount of times.
Michael Aaron Safyan
Yeah, I realized that right after submitting. Corrected my reply accordingly.
Ishpeck
+6  A: 

Try

coordinate_list = [Coordinates(location.latitude, location.longitude)
                   for location in locations 
                   for i in range(location.number_of_times)]
brainjam
I'd suggest `xrange` (iterator) instead of `range` (list)
David Zaslavsky
When I compared this with the answer I selected, I could not tell what they do different just by reading the comprehension. I actually had to write a test program to see what the difference was. This one creates unique Coordinate objects for each item in the list, whereas the other one reuses the same Coordinate object for repeated coordinates. Interesting.
User
`xrange` for Python 2.x, `range` for Python 3.x.
eksortso
@User, how do you tell the difference between unique and reused Coordinate objects?
brainjam
When __ repr __ is called it displays the memory location (I think) in HEX, with your solution I can see the Coordinate objects each have a unique memory location, with the other solution I can see that the repeated Coordinate objects share the same memory location.
User
@User, I found out that id() can also be used to see whether objects are distinct or not. Works for built-ins like numbers as well.
brainjam
+5  A: 
coordinate_list = [x for location in locations
                   for x in [Coordinates(location.latitude,
                                         location.longitude)
                            ] * location.number_of_times]

Edit: the OP suggests a loop may be clearer, which, given the length of the identifiers, is definitely a possibility. The equivalent code would then be something like:

coordinate_list = [ ]
for location in locations:
    coord = Coordinates(location.latitude, location.longitude)
    coordinate_list.extend([coord] * location.number_of_times)

The loop does look fine, partly because the extend method of lists works nicely here, and partly because you get to give a name to the Coordinate instance you're extending with.

Alex Martelli
You should also point out that this has issues when Coordinates is intended to be a mutable object.
Ants Aasma
Actually Ants comment lead me to choose this as the answer. This answer is preferable to me because it uses the same Coordinate objects which I assume uses less memory. In this case the Coordinate objects will not be changed.
User
Style question: would it be preferable to write this as a for loop? Is the comprehension too complex to be easily readable?
User
@User, it's not really complex, but the length of the identifiers hurts -- `[c for p in pts for c in [Coord(p.x, p.y)]*p.N]` (same structure, shorter names) would be OK, but names long enough as to force the code to take 3-4 lines makes it harder to parse. So, editing to show the alternative.
Alex Martelli