Set & Dict Comprehensions

Set Comprehensions

So we’ve learned about list comprehensions and generator expressions. What if we want to use a comprehension but create a set?

We could do this:

>>> words = ["apple", "orange", "lime", "lemon"]
>>> first_letters = set(w[0] for w in words)
>>> first_letters
{'o', 'a', 'l'}

Here we’ve used a generator expression and the set constructor to make a set.

But Python actually has a special syntax just for making set comprehensions:

>>> first_letters = {w[0] for w in words}
>>> first_letters
{'o', 'a', 'l'}

We use curly braces when making a set comprehension because we use curly braces when making a set.

Dict Comprehensions

What about making a dictionary from a generator expression?

Let’s say we have a dictionary where the values are numbers and we want to make a new dictionary which has only values with two digit numbers.

>>> favorite_numbers = {'rebecca': 293, 'ronald': 76, 'dorothy': 62, 'harold': 36, 'matt': 314}
>>> dict((k, v) for k, v in favorite_numbers.items() if v < 100)
{'ronald': 76, 'dorothy': 62, 'harold': 36}

Here we’re using a generator expression and a dict constructor to make a dictionary.

Python has a special syntax for creating dictionary comprehensions that we should use instead:

>>> {k: v for k, v in favorite_numbers.items() if v < 100}
{'ronald': 76, 'dorothy': 62, 'harold': 36}

Notice the k: v syntax which is very similar to the key-value syntax used for defining dictionaries.

Let’s create a dictionary where the keys are letters and the values are the indexes of those letters (where a is 1 and z is 26):

>>> from string import ascii_lowercase
>>> letters = {letter: n + 1 for n, letter in enumerate(ascii_lowercase)}
>>> letters['t']
20

We can also add an argument to enumerate so it will start at 1 instead of 0:

>>> letters = {letter: n for n, letter in enumerate(ascii_lowercase, start=1)}
>>> letters['t']
20

Now we can use this dictionary in a list comprehension to create a (not very) secret word encoding:

>>> word = "Trey"
>>> encoded_word = [letters[x] for x in word.lower()]
>>> encoded_word
[20, 18, 5, 25]

More Comprehension Exercises

Use some kind of comprehension to solve these exercises.

Flipped Dictionary

This is the flip_dict function in dictionaries.py.

Edit the function flip_dict, so that it flips dictionary keys and values, using a dictionary comprehension.

Example usage:

>>> from dictionaries import flip_dict
>>> flip_dict({'Python': "2015-09-15", 'Java': "2015-09-14", 'C': "2015-09-13"})
{'2015-09-15': 'Python', '2015-09-14': 'Java', '2015-09-13': 'C'}

Interleave

Edit the interleave function in the ranges.py file so that it accepts two iterables and returns a list with the items in each iterable “interleaved” (item 0 from iterable 1, then item 0 from iterable 2, then item 1 from iterable 1, and so on). Assume the two lists have the same length. To test it, run python test.py interleave in your exercises directory.

Example:

>>> from ranges import interleave
>>> interleave([1, 2, 3, 4], [5, 6, 7, 8])
[1, 5, 2, 6, 3, 7, 4, 8]
>>> interleave([1, 4, 7], [2, 5, 8])
[1, 2, 4, 5, 7, 8]

ASCII Strings

This is the get_ascii_codes function in dictionaries.py.

Edit the function get_ascii_codes so that it accepts a list of strings and returns a dictionary containing the strings as keys and a list of corresponding ASCII character codes as values.

>>> from dictionaries import get_ascii_codes
>>> words = ["hello", "bye", "yes", "no", "python"]
>>> get_ascii_codes(words)
{'hello': [104, 101, 108, 108, 111], 'bye': [98, 121, 101], 'yes': [121, 101, 115], 'no': [110, 111], 'python': [112, 121, 116, 104, 111, 110]}

Double-valued Dictionary

This is the dict_from_truple exercise we’ve seen before in dictionaries.py. The function accepts a list of three-item tuples and returns a dictionary where the keys are the first item of each tuple and the values are a two-tuple of the remaining two items of each input tuple.

Re-write this function to use a dictionary comprehension.

Example usage:

>>> from dictionaries import dict_from_truple
>>> dict_from_truple([(1, 2, 3), (4, 5, 6), (7, 8, 9)])
{1: (2, 3), 4: (5, 6), 7: (8, 9)}

Factors

This is the get_all_factors function in dictionaries.py.

Edit the function get_all_factors so that it takes a set of numbers and makes a dictionary containing the numbers as keys and all factors as values.

>>> from dictionaries import get_all_factors
>>> get_all_factors({1, 2, 3, 4})
{1: [1], 2: [1, 2], 3: [1, 3], 4: [1, 2, 4]}
>>> get_all_factors({62, 293, 314})
{314: [1, 2, 157, 314], 293: [1, 293], 62: [1, 2, 31, 62]}

Hint

You can use this function to find the factors of any number:

def get_factors(number):
    """Return a list of all factors of the given number."""
    return [
        n
        for n in range(1, number + 1)
        if number % n == 0
    ]

Multi-valued Dictionary

This is the dict_from_tuple exercise that we’ve seen before in dictionaries.py. The function accepts a list of tuples of any length and returns a dictionary which uses the first item of each tuple as keys and all subsequent items as values.

Re-write this function to use a dictionary comprehension.

Example usage:

>>> from dictionaries import dict_from_tuple
>>> dict_from_tuple([(1, 2, 3, 4), (5, 6, 7, 8)])
{1: (2, 3, 4), 5: (6, 7, 8)}
>>> dict_from_tuple([(1, 2, 3), (4, 5, 6), (7, 8, 9)])
{1: (2, 3), 4: (5, 6), 7: (8, 9)}

Most common

This is the get_most_common exercise in sets.py.

Edit the function get_most_common so that it accepts any number of sets and returns a set of the most common items from each of the given sets.

For example:

>>> from sets import get_most_common
>>> get_most_common([{1, 2}, {2, 3}, {3, 4}])
{2, 3}
>>> restaurants_trey = {'Habaneros', 'Karl Strauss', 'Opera', 'Punjabi Tandoor'}
>>> restaurants_diane = {'Siam Nara', 'Punjabi Tandoor', 'Opera'}
>>> restaurants_peter = {'Karl Strauss', 'Opera', 'Habaneros'}
>>> get_most_common([restaurants_trey, restaurants_diane, restaurants_peter])
{'Opera'}

Sorted Dictionary

Edit the sort_dict function in dictionaries.py that takes an input dictionary and returns a new dictionary that is sorted by the keys. Previously we created this function using for loops. Now we want to refactor it to use a dictionary comprehension rather than for loops.

Review: Note that as of Python 3.6, dictionaries are “in order”. What this means is that when you iterate over a dictionary, the entries you get are in the order that they were added to the dictionary. They might not have been added to the dictionary in an ordering that is useful. Once we have a dictionary, we want to make a new dictionary that has all the same key-value pairs, but are in sorted order when it is iterated over.

Hint: Use the built-in function sorted.

To test with the automated tests, run python test.py sort_dict from the command line.

To test the function in the REPL, you can paste the function in from your text editor or import it as shown:

>>> from dictionaries import sort_dict
>>> my_dict = {1: 'one', 6: 'six', 4: 'four', 3: 'three', 2: 'two', 5: 'five'}
>>> new_dict = sort_dict(my_dict)
>>> new_dict
{1: 'one', 2: 'two', 3: 'three', 4: 'four', 5: 'five', 6: 'six'}