Lab 9: Part 4. Map and Filter

A higher order function (HOF) is a function that either:

In Python, the built-in functions map and filter are higher order functions because they each take a function as a parameter.

Function signatures for map and filter taken from docs.python.org:

map(function, iterable, ...)

filter(function, iterable)

Task 1. map

This semester we've seen how to perform map operations (doing the same thing to each item in the list) by using loops or list comprehensions.

Today, we'll explore this same kind of technique but we'll use Python's map function.


Complete the following tasks in lab08/mapFilter.py, where you'll find some predefined lists you can use for testing purposes.

Task 1a. yell

Define a function yell that accepts a string and returns the string with the last letter repeated 5 times.

yell('cat') # => 'catttttt'

When yell is used with map, it should produce results like this:

map(yell, animals) # => ['catttttt', 'dogggggg', 'zebraaaaaa', 'pigggggg', 'bunnyyyyyy']
map(yell, superheroes) # => ['elektraaaaaa', 'firestarrrrrr', 'elastigirllllll', 'wonder womannnnnn', 'catwomannnnnn', 'batgirllllll']

Task 1b. flip

Define a function flip that accepts a string and returns that string reversed.

flip('cat') # => 'tac'

Hint: Recall the slicing syntax used to reverse a String: 'hello'[::-1] => 'olleh'

When flip is used with map, it should produce results like this:

map(flip, animals) # => ['tac', 'god', 'arbez', 'gip', 'ynnub']

map(flip, superheroes) # => ['artkele', 'ratserif', 'lrigitsale', 'namow rednow', 'namowtac', 'lrigtab']

Task 1c. double

Most of our examples today will focus on Strings, but map also works with numbers.

Define a function named double that multiplies a given number x 2.

double(2) # => 4

When double is used with map, it should produce results like this:

map(double, [1, 2, 3, 4, 5]) # => [2, 4, 6, 8, 10]

Task 2: Anonymous functions

The following is a typical Python function, defined using a style we've seen all semester:

def pluralize(x):
    return str(x) + 's'

In this example, def is used to define a function named pluralize.

Python provides a more succinct way of defining functions using something called lambda notation. The above function written in lambda notation would look like this:

lambda x: str(x) + 's'

Observations:


(Refer to Sorting and Lambda lecture notes, Slide #10 for more lambda notation information)


Anonymous functions are useful when working with higher order functions, where you often need a small helper function that you don't intend on using elsewhere in your code.

For example, instead of writing a definition of the function flip as you did above...

def flip(s):
    return s[::-1] # reverses a string

...you could use an anonymous function like this:

map(lambda s: s[::-1], superheroes)

Task 2a. Anonymous “yell”

Using lambda notation, complete the following code to produce the expected output:

map(???, animals) # => ['catttttt', 'dogggggg', 'zebraaaaaa', 'pigggggg', 'bunnyyyyyy']

Task 2b. Anonymous “flip”

Using lambda notation, complete the following code to produce the expected output:

map(???, animals) # => ['tac', 'god', 'arbez', 'gip', 'ynnub']

Task 3: Filter

Python's built-in filter function can be used to filter a sequence based on some provided criteria.

For example, we could filter the animals list to only include animals that contain the letter a.

Task 3a. containsA

To do this, start by defining a helper function called containsA that returns a Boolean value as to whether a given String contains the letter a.

containsA('cat') # => True
containsA('dog') # => False

When containsA is used with filter, it should produce results like this:

filter(containsA, animals) # => ['cat', 'zebra']
filter(containsA, fruit) # => ['apple', 'banana', 'strawberry', 'pomegranate']

Task 3b. mediumLength

Next, define a helper function called mediumLength that returns a Boolean value as to whether a given String is at least 4 characters long, but not longer than 7 characters long. In other words, the length of the string must be between 4 and 6, inclusive.

mediumLength('cat') # => False
mediumLength('zebra') # => True
mediumLength('pillow') # => True
mediumLength('encyclopedia') # => False

When mediumLength is used with filter, it should produce results like this:

filter(mediumLength, animals) # => ['zebra', 'bunny']
filter(mediumLength, fruit) # => ['apple', 'banana', 'cherry']

Task 4: Filter with anonymous functions

Using lambda notation, complete the tasks to produce the expected output.

Task 4a. Anonymous “containsA”

# Find words that contain the letter a
filter(???, fruit) # => ['apple', 'banana', 'strawberry', 'pomegranate']

Task 4b. Anonymous “mediumLength”

# Find words that are between 4 and 6 characters long
filter(???, animals) # => ['zebra', 'bunny']

Task 4c. Anonymous “mediumLengthWithA”

# Find words that contain 'a' and are between 4 and 6 characters long
filter(???, animals) # => ['zebra']

Task 5. Consecutive letters in a string

Task 5a. consecLetters

To start, we want to define a function called consecLetters which accepts a String and returns True if it contains two consecutive letters that are the same; False otherwise.

consecLetters('bunny')
# => True, b/c there are two consecutive letters: 'nn'

consecLetters('cat')
# => False, no consecutive letters

consecLetters('Jessica')
# => True, b/c there are two consecutive letters: 'ss'

We'll break this function down into three steps, so copy the following scaffold into your lab08/mapFilter.py file:

def consecLetters(theString):

    #### Step 1 ####
    # Make pairs of all the letters
    # [...Your code here...]

    #### Step 2 ####
    # Using `map`, create a list of True/False values for each pair, regarding whether it's a match (True) or not (False)
    # [...Your code here...]

    #### Step 3 ####
    # Using `any`, return True if at least one of the pairs was a match (i.e. True)
    # [...Your code here...]

Read through the comments to see the “big picture” of how consecLetters will work.

The following hints walk you through completing each step.

Step 1 Hint

Use zip to create pairs of neighboring letters in a word (which will be useful when searching for consecutive leters).

Example usage of zip:

letterPairs = zip('bunny', 'bunny'[1:]) # => [('b', 'u'), ('u', 'n'), ('n', 'n'), ('n', 'y')]

Step 2 Hint

If we think about what consecLetters is trying to accomplish, only one of the tuples in the list created by zip needs to contain a match in order for consecLetters to return True.

For example, bunny would qualify because at least one of the pairs has consecutive letters (('n', 'n')):

# [('b', 'u'), ('u', 'n'), ('n', 'n'), ('n', 'y')]

To programmatically identify a matching pair, we could use for loop to look at each tuple, but since our focus is on higher-order-functions today, we'll use map to create a list of True/False values as to whether each pair is a match or not.

The following map function can be completed to accomplish this step. (It's up to you if you want to define a helper function or use lambda notation to create an anonymous function.)

map(???, letterPairs) # => [False, False, True, False]

Step 3 Hint

At this point, the results of the previous step should give you a list of Boolean values, indicating which letter pairs are matching (True) or not (False).

We only need one of those pairs to be matching in order to meet the requirements for “consecutive letters”, so we can call upon Python's built-in function any which returns True if at least one item in the list is True; Otherwise it returns False.

Example uses of any:

any([True, False, False]) # => True
any([False, False, False, False, False]) # => False
any([False, False, False, True, False, True]) # => True

Test consecLetters

Once you've completed the three steps in consecLetters, you can test your work:

consecLetters('bunny') # => True
consecLetters('cat') # => False
consecLetters('book') # => True

Task 5b.

With consecLetters working, we can now use it to filter a list of words down to only words that contain consecutive letters.

list(filter(consecLetters, animals)) # => ['bunny']
list(filter(consecLetters, fruit)) # => ['apple', 'cherry', 'strawberry']

You can import a list of names of all CS111 students like this:

from names import names

And then you can test as follows:

list(filter(consecLetters, names))

should return this list:

['Leen', 'Chantelle', 'Kerry', 'Jesslyn', 'Isabella', 'Hannah', 'Isabelle',
  'Jisoo', 'Vanessa','Terri', 'Ann', 'Tiffany', 'Stella', 'Tyanna', 'Annie', 
  'Michelle', 'Michaella', 'Colleen', 'Pallavi', 'Jessica', 'Rebecca', 
  'Katianna', 'Jessica', 'Michaella', 'Leanne', 'Anna', 'Jessica']

Table of Contents