Reputation: 4726
I've got a bunch of list comprehensions that I'd like to combine.
I've got a list of objects, and I want to generate a set of lists, each of which contains some property of each of the objects in the original list. For example:
my_list = ["Kathryn", "John", "Eve", "Jack"]
initials = [x[0] for x in my_list]
upper_case = [x.upper() for x in my_list]
lower_case = [x.lower() for x in my_list]
Note that I'm looping through my original list multiple times. If I have many different properties I want to get from each element, then I'm going to be repeating this looping a lot.
Instead, I tried zipping the results of getting all properties in one single iteration:
initials, upper_case, lower_case = zip(*((x[0], x.upper(), x.lower()) for x in my_list))
This works, except (1) I think this code is rather unclear, and (2) the resulting variables are actually tuples instead of lists, so to get lists (which I need) I'd have to do something like
initials, upper_case, lower_case = (list(x) for x in (initials, upper_case, lower_case))
which, already right now, but especially if I'm extracting more than three properties, I also don't really like the looks of. ("... of which I also don't really like the looks"?)
Is there a more satisfactory, "cleaner" way to do this?
Upvotes: 0
Views: 129
Reputation: 1570
What you need is map the functions to the list to get another list with the same shape. You can specify the functions as lambdas (anonymous functions) or use methodcaller/itemgetter as the solution of jpp
my_list = ["Kathryn", "John", "Eve", "Jack"]
initials = list(map(lambda x:x[0] ,my_list))
upper_case = list(map(lambda x:x.upper() ,my_list))
lower_case = list(map(lambda x:x.lower() ,my_list))
Upvotes: 0
Reputation: 39414
Another DRY way of doing this is:
def comprehend(func, iterable):
return [func(item) for item in iterable]
my_list = ["Kathryn", "John", "Eve", "Jack"]
Initials = comprehend(lambda x:x[0], my_list)
Upper_case = comprehend(lambda x:x.upper(), my_list)
Lower_case = comprehend(lambda x:x.lower(), my_list)
Upvotes: 0
Reputation: 164823
The issues you are considering: clarity, tuple vs list output, extendibility to more functions.
You may wish to consider a functional solution, which could add clarity and extendibility. Such a solution may not be faster than other solutions. Here's an example:
from operator import itemgetter, methodcaller
my_list = ['Kathryn', 'John', 'Eve', 'Jack']
funcs = (itemgetter(0), methodcaller('upper'), methodcaller('lower'))
i, u, l = map(list, (map(func, my_list) for func in funcs))
print(i, u, l, sep='\n')
['K', 'J', 'E', 'J']
['KATHRYN', 'JOHN', 'EVE', 'JACK']
['kathryn', 'john', 'eve', 'jack']
Upvotes: 0
Reputation: 77942
Plain simply, use a for
loop instead:
my_list = ["Kathryn", "John", "Eve", "Jack"]
initials = []
upper_case = []
lower_case = []
for x in my_list:
initials.append(x[0])
upper_case.append(x.upper())
lower_case.append(x.lower())
As a general rule: the first and foremost goal of list comprehensions is not to be faster but to be more readable (by reducing "line noise"). If using a list comprehension makes your code less readable, then don't use a list comprehension.
NB: this assumes you want to optimize for readability of course - sometimes you do want to optimize for speed even if it makes for less readable code, and that's ok too (as long as you compensate with clear comments / documentation so you don't want to hang yourself to the nearest tree when you have to maintain that code a few months later).
Upvotes: 2