List Functions

circuitpython_vita-marija-murenaite-691144-unsplash.jpg
Photo by Vita Marija Murenaite on Unsplash

Slicing

We already saw how you can access items in a list using the list[index] syntax. This is the simplest case: it gives you a single item. The more general form of the syntax allows you to extract a contiguous subset of the list. 

You can specify the index (starting at 0, remember) of the first item (i.e. inclusive), and the index of the item one past the last one you want (i.e. exclusive). For example l[0:len(l)] will give you the entire list.

Download: file
l = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
l[3]
#'d'
l[3:6]
#['d', 'e', 'f']
l[0:len(l)]
#['a', 'b', 'c', 'd', 'e', 'f', 'g']

If you are interested in items from the first or last of the list, it can be simplified to omit the 0 or len(l):

Download: file
l[:4]
#['a', 'b', 'c', 'd']
l[4:]
#['e', 'f', 'g']

Filtering

The filter function takes a predicate (i.e. a function that returns a boolean value) and a list. filter returns a filter object that generates a list that contains only those elements from the original list for which the predicate returns True. Filter predicates can either be a reference to a named function or, quite often, a lambda.

Say you want to pick out only the numbers in a list that are multiples of 3. If a is your original list of numbers, say the result of range(20):

Download: file
a = range(20)
list(filter(lambda x: x % 3 == 0, a))
#[0, 3, 6, 9, 12, 15, 18]

Notice the need to use list to convert the filter object to a list.

Mapping

Another common thing to do with lists is to perform some operation on each item, accumulating the results into a new list. The map function does this. It takes a function (which is often a lambda)  and the original list. Similar to filter, map returns a map object.

Let's take the same list a, and generate a list of the squares of its contents.

Download: file
list(map(lambda x: x**2, a))
#[0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196, 225, 256, 289, 324, 361]

map isn't limited to processing a single list; you can pass it any number of lists, with the restriction that the function has to have that number of parameters.

Download: file
list(map(lambda x, y: x + y, [0,1,2,3], [4,5,6,7]))
#[4, 6, 8, 10]

We can combine map with filter to process a subset of a list. Let's get the squares of the multiple of 3:

Download: file
list(map(lambda x: x**2, filter(lambda x: x % 3 == 0, a)))
#[0, 9, 36, 81, 144, 225, 324]

While this works, it can be a bit cumbersome at times. Thankfully Python provides a better alternative, which we will examine shortly.

Reducing

The third function we're going to look at is reduce. It's different than filter and map in that it doesn't result in a sequence of values. Instead it takes a list, a function, and an optional initial value and uses them to compute a single value. It, in essence, reduces the list to a single value.

Unlike filter and map, reduce isn't a built-in; it's in the functools module. There are plenty of other fun, useful things in that module but we won't be looking at those now. This module isn't included with CircuitPython, but it is with MicroPython. You can find it at https://github.com/micropython/micropython-lib. To use it with CircuitPython, copy micropython-lib/functools/functools.py (either from a clone of the repository or just the single downloaded file) to the CIRCUITPY drive. Once it's there you'll need to import it:

Download: file
from functools import reduce

reduce is sometimes confusing at first, but it's actually quite simple. The function you pass it takes two parameters; let's call them accumulator and x. This function is called for items of the list (in order), that's the second (x) argument. The first argument (accumulator) is the result from the previous time the function was called.

The first time through is a little different. If the optional third argument is supplied to reduce, it is used as the result of the previous call (as mentioned above). If it's omitted, the first value from the list is used instead.

Some examples should illustrate it:

Download: file
reduce(lambda x, y: x+y, [1, 2, 3, 4, 5])

this is equivalent to:

Download: file
((((1+2)+3)+4)+5)

If, instead, we had:

Download: file
reduce(lambda x, y: x+y, [1, 2, 3, 4, 5], 10)

it would be the same as:

Download: file
(((((10+1)+2)+3)+4)+5)

We can give it a try and see:

Download: file
reduce(lambda x,y:x+y,[1,2,3,4,5])
#15
reduce(lambda x,y:x+y,[1,2,3,4,5], 10)
#25

Note: the type of the result of reduce will be the type of the initial value, either the one supplied or the first item in the list.

Of course, like filter and map, you don't need a lambda. You can use the name of a function if there's one that does exactly what you want.  Since Python lambdas are limited to a single expression, you might sometimes need to write a function just to be used for one of these functions.

How about another example.

Download: file
def prepend(l, x):
    l.insert(0, x)
    return l

reduce(prepend, ['a', 'b', 'c'], [])
# returns ['c', 'b', 'a']

We can't use a lambda here because insert doesn't return the list. We need to do that as a second step, which we can't do in a lambda. Also, we need to provide the empty list as the initial accumulator value so that

  1.  the result will be a list, and
  2.  there's something to insert the first item into.
This guide was first published on Oct 11, 2018. It was last updated on Oct 11, 2018.
This page (List Functions) was last updated on Jul 14, 2020.