Tiny Python Projects Jump the Five (Working with Dictionaries) in 2020

    Clark-TPP-MEAP-HI

    From Tiny Python Projects by Ken Youens-Clark

    In this article, you will Learn to:

    • Create a dictionary.
    • Use a for loop to process text character-by-character.
    • Check if items exist in a dictionary.
    • Retrieve values from a dictionary.

    Print a new string with the numbers substituted for their encoded values.

    You can find videos relevant to this exercise on youtube. You can also get the book for 40% off by entering nlpyblog40 into the discount code box at checkout at manning.com.

    “When I get up, nothing gets me down.”
    – D. L. Roth

    In an episode of the television show The Wire, drug dealers encode telephone numbers that they text in order to obscure them from the police who they assume are intercepting their messages. They use an algorithm we’ll call “Jump the Five” where a number is changed to the one that is opposite on a US telephone pad if you jump over the 5.

    numbers

    If we start with “1” and jump across the 5, we get to “9,” then “6” jumps the 5 to become “4,” and so forth. The numbers “5” and “0” will swap with each other. In this exercise, we’re going to write a Python program called jump.py that will take in some text as a positional argument. Each number in the text will be encoded using this algorithm. All non-number will pass through unchanged, for example:

    $ ./jump.py 867-5309

    243-0751

    $ ./jump.py ‘Call 1-800-329-8044 today!’
    Call 9-255-781-2566 today!

    We will need some way to inspect each character in the input text and identify the numbers. We will learn how to use a for loop for this and how that relates to a “list comprehension.” Then we will need some way to associate a number like “1” with the number “9,” and so on for all the numbers. We’ll learn about a data structure in Python called a “dictionary” type that allows us to do exactly that.

    Before we get started with the coding, let’s spend some time learning about dictionaries.

    Dictionaries

    A Python dict allows us to relate something (a “key”) to some other thing (a “value”). An actual dictionary does this. If we look up a word like “quirky” in a dictionary (https://www.merriam-webster.com/dictionary/quirky), we can find a definition. We can think of the word itself as the “key” and the definition as the “value.”

    quirky.2

    Dictionaries actually provide quite a bit more information such as pronunciation, part of speech, derived words, history, synonyms, alternate spellings, etymology, first known use, etc. (I really love dictionaries.) Each of those attributes has a value, so we could also think of the dictionary entry for a word itself as another “dictionary”:

    quirky

    Let’s see how we can use Python’s dictionaries to go beyond word definitions.

    python-dictionary

    Creating a Dictionary

    In the film Monty Python and the Holy Grail, King Arthur and his knights must cross the Bridge of Death. Anyone who wishes to cross must correctly answer three questions posed by the Keeper. Those who fail are cast into the Gorge of Eternal Peril. Let’s create and use a dict to keep track of the questions and answers as key/value pairs.

    creating-a-dictoinary

    Lancelot goes first. We can either use the dict() function to create an empty dictionary for his answers. Once again, I want you to fire up your python3 / ipython REPL or Jupyter Notebook and type these out for yourself!

    >>> answers = dict()

    Or we can use empty curly brackets:

    >>> answers = {}

    holy-shield

    The Keeper’s first question is: “What is your name?” Lancelot answers “My name is Sir Lancelot of Camelot.” We can add the key “name” to our answers by using square brackets ( []  — not curlies!) and the literal string ‘name’ :

    >>> answers[‘name’] = ‘Sir Lancelot’

    If you type answers<Enter> in the REPL, Python will show you a structure in curlies to indicate this is a dict :

    >>> answers

    holy-grail

    {‘name’: ‘Sir Lancelot’}

    You can verify with the type function:

    >>> type(answers)

    <class ‘dict’>

    Next the Keeper asks, “What is your quest?” to which Lancelot answers “To seek the Holy Grail.” Let’s add “quest” to the answers:

    >>> answers[‘quest’] = ‘To seek the Holy Grail’

    There’s no return value to let us know something happened, so we can inspect the variable again to ensure our new key/value was added:

    >>> answers

    {‘name’: ‘Sir Lancelot’, ‘quest’: ‘To seek the Holy Grail’}

    Finally, the Keeper asks “What is your favorite color?,” and Lancelot answers “blue.”

    >>> answers[‘favorite_color’] = ‘blue’

    >>> answers

    {‘name’: ‘Sir Lancelot’, ‘quest’: ‘To seek the Holy Grail’, ‘favorite_color’: ‘blue’}

    If you knew all the answers beforehand, you could create answers using the dict() function with this syntax where you do not have to quote the keys and the keys are separate from the values with equal signs:

    >>> answers = dict(name=’Sir Lancelot’, quest=’To seek the Holy Grail’, favorite_color=’blue’) Or this syntax using curlies {} where the keys must be quoted and are followed by a colon ( : ):

    >>> answers = {‘name’: ‘Sir Lancelot’, ‘quest’: ‘To seek the Holy Grail’, ‘favorite_color’: ‘blue’}

    It might be helpful to think of the dictionary answers as a box that inside holds the key/value pairs that describe Lancelot’s answers.

    answers

    Accessing Dictionary Values

    To retrieve the values, you use the key name inside square brackets ( [] ). For instance, I can get the name like so:

    >>> answers[‘name’]

    ‘Sir Lancelot’

    You will cause an exception if you ask for a dictionary key that doesn’t exist!

    other-dic-method3

    >>> answers[‘age’]

    Traceback (most recent call last):

      File “<stdin>”, line 1, in <module>
    KeyError: ‘age’

    Just as with lists, you can use the x in y to first see if a key exists in the dict:

    >>> ‘quest’ in answers

    True

    >>> ‘age’ in answers

    False

    other-dic-method2

    The dict.get() method is a safe way to ask for a value:

    >>> answers.get(‘quest’)

    ‘To seek the Holy Grail’

    When the requested key does not exist in the dict, it will return the special value None:

    >>> answers.get(‘age’)

    That doesn’t print anything because the REPL won’t print a None, but we can check the type:

    >>> type(answers.get(‘age’))

    <class ‘NoneType’>

    There is an optional second argument you can pass to dict.get() which is the value to return if the key does not exist:

    >>> answers.get(‘age’, ‘NA’)

    ‘NA’

    other-dic-method

    Other Dictionary Methods

    If you want to know how “big” a dictionary is, the len (length) function on a dict will tell you how many key/value pairs are present:

    >>> len(answers)

    3

    The dict.keys method will give you just the keys:

    >>> answers.keys() dict_keys([‘name’, ‘quest’, ‘favorite_color’])

    And dict.values will give you the values:

    >>> answers.values() dict_values([‘Sir Lancelot’, ‘To seek the Holy Grail’, ‘blue’])

    Often we want both together, so you might see code like this:

    >>> for key in answers.keys():

    …     print(key, answers[key])


    name Sir Lancelot quest To seek the Holy Grail favorite_color blue

    An easier way to write this would be to use the dict.items() method which will return a list of the key/value pairs:

    >>> answers.items()

    dict_items([(‘name’, ‘Sir Lancelot’), (‘quest’, ‘To seek the Holy Grail’), (‘favorite_color’, ‘blue’)])

    The above for loop could also be written using dict.items() and also a bit of string formatting to make the key be printed in a column 15 characters wide:

    >>> for key, value in answers.items(): …     print(f'{key:15} {value}’)


    name            Sir Lancelot
    quest           To seek the HolyGrail   favorite_color  blue

    In the REPL you can execute help(dict) to see all the methods available to you like pop to remove a key/value or update to merge with another dict .

    Each key in the dict is unique. That means if you set a value for a given key twice:

    >>> answers = {}

    >>> answers[‘favorite_color’] = ‘blue’

    >>> answers

    {‘favorite_color’: ‘blue’}

    You will not have two entries but one entry with the second value:

    >>> answers[‘favorite_color’] = ‘red’

    >>> answers

    {‘favorite_color’: ‘red’}

    Keys don’t have to be strings — you can also use numbers like int and float . Whatever value you use must be immutable. A list could not be a key because it is mutable, but a tuple (which is an immutable list ) can be a key.

    Writing jump.py

    Now let’s get started with writing our program. Here is a diagram of the inputs and outputs. Note that your program will only affect the numbers in the text. Anything that is not a number is unchanged:

    jumpy.py

    When run with no arguments or -h|–help, your program should print a usage:

    $ ./jump.py -h

    usage: jump.py [-h] str

    Jump the Five

    positional arguments:   str         Input text

    optional rguments:

      -h, –help  show this help message and exit Here is the substitution table for the numbers:

    1. => 9
    2. => 8
    3. => 7
    4. => 6
    5. => 0
    6. => 4
    7. => 3
    8. => 2
    9. => 1

    0 => 5

    solution-table

    How would you represent this using a dict ? Try creating a dict called jump in the REPL and then using a test. Remember that assert will return nothing if the statement is True :

    >>> assert jumper[‘1’] == ‘9’

    >>> assert jumper[‘5’] == ‘0’

    Next, you will need a way to visit each character in the given text. I suggest you use a for loop like so:

    >>> for char in ‘ABC123’: …     print(char)

    A

    B

    C

    1

    2

    3

    Now, rather printing the char, print the value of char in the jumper table or print the char itself. Look at the

    dict.get() method! Also, if you read help(print), you’ll see there is an end option to change the newline that gets stuck

    onto the end to something else.

    Hints:

     The numbers can occur anywhere in the text, so I recommend you process the input character-by-character with a for loop.

    • Given any one character, how can you look it up in your table?
    • If the character is in your table, how can you get the value (the translation)?
    • If how can you print() the translation or the value without printing a newline? Look at help(print) in the REPL to read about the options to print().
    • If you read help(str) on Python’s str class, you’ll see that there is a str.replace() method. Could you use that?

    Now spend the time to write the program on your own before you look at the solutions! Use the tests to guide you.

    Solution

    #!/usr/bin/env python3

    “””Jump the Five”””

    import argparse

    # ————————————————–

    def get_args():                                                    1

        “””Get command-line arguments”””

        parser = argparse.ArgumentParser(

            description=’Jump the Five’,

            formatter_class=argparse.ArgumentDefaultsHelpFormatter)

        parser.add_argument(‘text’, metavar=’text’, help=’Input text’) 2

        return parser.parse_args()

    # ————————————————–

    def main():                                                        3

        “””Make a jazz noise here”””

        args = get_args()                                              4

        jumper = {‘1’: ‘9’, ‘2’: ‘8’, ‘3’: ‘7’, ‘4’: ‘6’, ‘5’: ‘0’,    5

                  ‘6’: ‘4’, ‘7’: ‘3’, ‘8’: ‘2’, ‘9’: ‘1’, ‘0’: ‘5’}

        for char in args.text:                                         6

            print(jumper.get(char, char), end=”)                      7

        print()                                                        8

    # ————————————————–

    if __name__ == ‘__main__’:                                         9

    1. main()Define the get_args() function first so it’s easy to see when we read the program.
    2. We define one positional argument called text.
    3. Define a main() function where the program starts.
    4. Get the command-line args.
    5. Create a dict for the lookup table.
    6. Process each character in the text.
    7. Print either the value of the char in the jumper table or the char if it’s not present, making sure not to print a newline by adding end=”.
    8. Print a newline.
    9. Call the main() function if the program is in the “main” namespace.

    That’s all for this article. See more of the book’s contents on our browser-based live book platform here.