Summary: This blog explains the ways to apply a given function to each element of a list. The best way to apply a function to each element of a list is to use the Python built-in map()
function that takes a function and one or more iterables as arguments. It then applies the function to each element of the iterables. An alternate way is to use list comprehension.Β
Note: All the solutions provided below have been verified using Python 3.9.0b5
Problem Formulation
Imagine the following list of strings in Python.
my_list = ['you', 'cannot', 'exercise', 'away', 'a', 'bad', 'diet']
How does one apply a function to uppercase each string within the list?
['YOU', 'CANNOT', 'EXERCISE', 'AWAY', 'A', 'BAD', 'DIET']
Background
The above problem, like many others, has quite a simple solution in Python. One could write a plain vanilla C-like loop to solve this. Yet, almost every Python coder will get bored with C-like loops, at one point or another. They often try to find easier, faster and elegant solutions. Luckily Python as a scripting language, is always evolving to please its fans. The Python community strives to offer elegant and creative ways to solve problems. The purpose of these blog tutorials is to provide the user with ideas, to solve bigger problems.
Letβs Start, I Am Eager to Learn!!
Using the Python built-in map()
function is the most efficient and elegant way to solve the problem. map()
takes a function and one or more iterables as arguments.Β map()
applies the given function to each element of the iterable arguments. The following code shows different variations of the solution.
>>> ## 'my_list' is the original list whose string elements need to be >>> ## fully uppercased. Note that 'my_list' is an object of the Python >>> ## built-in List class. Lists, Sets, Dicts and Tuples are considered >>> ## iterables. >>> my_list = ['you', 'cannot', 'exercise', 'away', 'a', 'bad', 'diet'] >>> >>> ## Use the upper() function of Python's built-in str class, to modify >>> ## each element of the my_list iterable. >>> my_generic_iterable = map(str.upper, my_list) >>> >>> ## map() returns a pure iterable object. It is also known as a generator object. >>> ## It contains all the modified elements. Generators are temporary container >>> ## objects. They can be iterated upon only once, to extract the elements >>> ## within them. For example, use the 'list()' constructor to go thru each >>> ## element of the 'my_generic_iterable' generator and generate a list. >>> list(my_generic_iterable) ['YOU', 'CANNOT', 'EXERCISE', 'AWAY', 'A', 'BAD', 'DIET'] >>> >>> ## Alternatively one can iterate thru each element in my_generic_iterable >>> ## and print it. But one has to create it again... >>> ## Go ahead and try commenting the line below (i.e. map()...) to see what >>> ## gets printed with/without executing it. >>> my_generic_iterable = map(str.upper, my_list) >>> for i in my_generic_iterable: ... print(i) ... YOU CANNOT EXERCISE AWAY A BAD DIET >>>
Not so Fast!! What Did You Mean By One Or More Iterable Arguments To map()?
Ha!! You noticed that!!! The map()
function can have more than one iterable as arguments. What does this mean? Consider the following code.
>>> ## Lets consider the following two list of strings. They need to be sewn >>> ## together and uppercased as in the previous example. >>> my_list1 = ['you', 'exercise', 'a', 'diet', 'eat', 'hearty', 'once', 'day'] >>> my_list2 = ['cannot','away','bad', '!! ', 'a', 'salad', 'a', '.'] >>> >>> ## A function func1() is defined to join the two string elements and uppercase the resulting >>> ## string. >>> def func1 (item1, item2): ... return str.upper(item1 + ' ' + item2) ... >>> ## This time the map() function is given the function (i.e. func1()) and two lists as >>> ## arguments. As before, a generic Iterator is returned. >>> iter = map(func1, my_list1, my_list2) >>> >>> ## Extract the elements from the iterator. Voila!! The string elements are sewn together >>> ## and uppercased. >>> list(iter) ['YOU CANNOT', 'EXERCISE AWAY', 'A BAD', 'DIET !! ', 'EAT A', 'HEARTY SALAD', 'ONCE A', 'DAY .'] >>> ## Here is a bonus one-liner to figure out. Hint: This is the same as the above two lines >>> ## combined together into one line. >>> list(map(func1, my_list1, my_list2)) ['YOU CANNOT', 'EXERCISE AWAY', 'A BAD', 'DIET !! ', 'EAT A', 'HEARTY SALAD', 'ONCE A', 'DAY .'] >>>
Everything is almost the same as in the previous example. The noticeable differences are
- The function is a formal definition (i.e.
func1()
) instead of a built-in (e.g.str.upper
) map()
gets two iterable arguments this time around (i.e.my_list1
,my_list2
)
map()
still returns an iterator. A function is a function, whether it is a built-in or an explicit definition.Β And yes, one can provide even three or four or more iterable arguments to the map()
function, if needed.
Ok Great!! You Also Mentioned List Comprehension
Good, you noticed that!! Yes, one can use list comprehension to apply a function to each element of a list. Like map()
, a list comprehension is also easy to see and understand. Although, the map()
function does execute faster than list comprehension. Consider the following code.
>>> ## 'my_list' is the original list, whose string elements need to be >>> ## fully uppercased. Note that 'my_list' is an object of the Python >>> ## built-in List class. Lists, Sets, Dicts and Tuples are considered >>> ## iterables. >>> my_list = ['you', 'cannot', 'exercise', 'away', 'a', 'bad', 'diet'] >>> >>> ## Unlike map(), this list comprehension returns a List. >>> my_list1 = [str.upper(i) for i in my_list] >>> >>> ## Print the resulting list. As you can see the result is the same as map() >>> print(my_list1) ['YOU', 'CANNOT', 'EXERCISE', 'AWAY', 'A', 'BAD', 'DIET'] >>> >>> ## Here is a one-liner to ponder. It does the exact same operation as >>> ## above. Don't worry if you do not understand it immediately!! Take a >>> ## deep breath, stare at it, take it apart operation by operation... >>> ## You will eventually get the hang of it with enough practice. >>> [str.upper(i) for i in ['eating', 'a', 'hearty', 'vegetable', 'stew', 'heals', 'the', 'body', 'and', 'the', 'soul']] ['EATING', 'A', 'HEARTY', 'VEGETABLE', 'STEW', 'HEALS', 'THE', 'BODY', 'AND', 'THE', 'SOUL'] >>>
One can see that the list comprehension is just as easy to use as the map()
function.
Hmm!! Which One Should I Use?
Excellent question!! Consider the following comparison. Note the C-like βforβ loop for extra amusement.Β
$ ## Lets start with the C-like for loop. $ python -m timeit \ > "my_list = []" \ > "for i in ['you', 'cannot', 'exercise', 'away', 'a', 'bad', 'diet']: \ > my_list.append(i.upper())" 200000 loops, best of 5: 1.13 usec per loop $ ## Next up is list comprehension. $ python -m timeit \ > "[i.upper() for i in ['you', 'cannot', 'exercise', 'away', 'a', 'bad', 'diet']]" 500000 loops, best of 5: 992 nsec per loop $ ## Finally, use the map() function. $ python -m timeit \ > "list(map(str.upper, ['you', 'cannot', 'exercise', 'away', 'a', 'bad', 'diet']))" 500000 loops, best of 5: 894 nsec per loop
As one can see, there is only a minor difference between the three methods. The map()
Β function executes the fastest among the three. For the small and easy example shown above, either of the three methods is fine to use. But as the lists or functions become complex, map()
becomes the more practical method to use.Β
Conclusion
As a scripting language, Python is always evolving in interesting ways.Β The Python developer community is quite active about adding new and better features.Β They continue to improve the existing features.Β Learning is a life-long commitment and should never end. Consider the following code that modifies the original list in place. Yes, just when you, the reader, thought this article was over!!!
>>> ## 'my_list' is the same original list as before >>> my_list = ['you', 'cannot', 'exercise', 'away', 'a', 'bad', 'diet'] >>> my_list ['you', 'cannot', 'exercise', 'away', 'a', 'bad', 'diet'] >>> >>> ## This is the function to use. It takes a list and an index. >>> ## func1() capitalizes the first letter of each string >>> ## element at the i'th index of the list. The capitalization >>> ## is done by using the title() function from Python's built-in >>> ## str module >>> def func1(alist, i): ... alist[i] = alist[i].title() ... >>> ## This time around, just the first letter of each string element >>> ## needs to be capitalized. The need was to modify the original list >>> ## in-place. The Python built-in any() function simply forces map() >>> ## to iterate thru iterate thru the numeric array created by range(). >>> ## One could have used list(), set(), all() etc. to force such an >>> ## iteration. Try it!! >>> any(map(lambda i:func1(my_list, i), range(len(my_list)))) False >>> >>> ## The proof is in the pudding!!! >>> my_list ['You', 'Cannot', 'Exercise', 'Away', 'A', 'Bad', 'Diet'] >>>
Finxter Academy
This blog was brought to you by Girish Rao, a student of Finxter Academy. You can find his Upwork profile here.
Reference
All research for this blog article was done using Python Documents, the Google Search Engine and the shared knowledge-base of the Finxter Academy and the Stack Overflow Communities.