CMU 15-112: Fundamentals of Programming and Computer Science
Class Notes: Dictionaries


  1. Quick Example
  2. Creating Dictionaries
  3. Using Dictionaries
  4. Properties of Dictionaries
    1. Dictionaries Map Keys to Values
    2. Keys are Sets
    3. Values are Unrestricted
    4. Dictionaries are Very Efficient
  5. Some Worked Examples Using Dictionaries

  1. Quick Example
    # A dictionary is a data structure that maps keys to values in the same way # that a list maps indexes to values. However, keys can be any immutable value! stateMap = { 'pittsburgh':'PA', 'chicago':'IL', 'seattle':'WA', 'boston':'MA' } city = input("Enter a city name --> ").lower() if (city in stateMap): print(city.title(), "is in", stateMap[city]) else: print("Sorry, never heard of it.")

    Another Example:
    counts = dict() while True: n = int(input("Enter an integer (0 to end) --> ")) if (n == 0): break if (n in counts): counts[n] += 1 else: counts[n] = 1 print("I have seen", n, "a total of", counts[n], "time(s)") print("Done, counts:", counts)

  2. Creating Dictionaries
    • Create an empty dictionary
      d = dict() print(d) # prints {} # We can also use empty braces d = { } print(d) # prints {}

    • Create a dictionary from a list of (key, value) pairs
      pairs = [("cow", 5), ("dog", 98), ("cat", 1)] d = dict(pairs) print(d) # unpredictable order!

    • Statically-allocate a dictionary
      d = { "cow":5, "dog":98, "cat":1 } print(d) # ditto!

  3. Using Dictionaries
    # We can interact with dictionaries in a similar way to lists/sets d = { "a" : 1, "b" : 2, "c" : 3 } print(len(d)) # prints 3, the number of key-value pairs print("a" in d) # prints True print(2 in d) # prints False - we check the keys, not the values print(2 not in d) # prints True print("a" not in d) # prints False print(d["a"]) # finds the value associated with the given key. Crashes if the key is not in d print(d.get("z", 42)) # finds the value of the key if the key is in the dictionary, # or returns the second (default) value if the key is not in d d["e"] = "wow" # adds a new key-value pair to the dictionary, or updates the value of a current key del d["e"] # removes the key-value pair specified from the dictionary. Crashes if the key is not in d for key in d: print(key, d[key]) # we can iterate over the keys, then print out the keys or corresponding values

  4. Properties of Dictionaries
    1. Dictionaries Map Keys to Values
      ages = dict() key = "fred" value = 38 ages[key] = value # "fred" is the key, 38 is the value print(ages[key])

    2. Keys are Sets
      • Keys are unordered
        d = dict() d[2] = 100 d[4] = 200 d[8] = 300 print(d) # unpredictable order

      • Keys are unique
        d = dict() d[2] = 100 d[2] = 200 d[2] = 400 print(d) # { 2:400 }

      • Keys must be immutable
        d = dict() a = [1] # lists are mutable, so... d[a] = 42 # Error: unhashable type: 'list'

    3. Values are Unrestricted
      # values may be mutable d = dict() a = [1,2] d["fred"] = a print(d["fred"]) a += [3] print(d["fred"]) # sees change in a! # but keys may not be mutable d[a] = 42 # TypeError: unhashable type: 'list'

    4. Dictionaries are Very Efficient
      As mentioned above, a dictionary's keys are stored as a set. This means that finding where a key is stored takes constant time. This lets us look up a dictionary's value based on a key in constant time too!

  5. Some Worked Examples Using Dictionaries
    • isAnagram(s1, s2)
      Here we rewrite the 1d-list isAnagram example only using a dictionary instead.
      from collections import defaultdict #: Option 1: initializing d[key] to 0: def letterCounts(s): counts = dict() for ch in s.upper(): if ch.isalpha(): if ch not in counts: counts[ch] = 0 counts[ch] += 1 return counts # Option 2: using d.get(key, defaultValue): def letterCounts(s): counts = dict() for ch in s.upper(): if ch.isalpha(): counts[ch] = counts.get(ch, 0) + 1 return counts # Option 3: using defaultdict(lambda: defaultValue) # Note that defaultdict is not part of the # required 112 subset, but it is very handy. def letterCounts(s): counts = defaultdict(lambda: 0) for ch in s.upper(): if ch.isalpha(): counts[ch] += 1 return counts # Regardless, this makes isAnagram clear and efficient: def isAnagram(s1, s2): return (letterCounts(s1) == letterCounts(s2)) def testIsAnagram(): print("Testing isAnagram()...", end="") assert(isAnagram("", "") == True) assert(isAnagram("abCdabCd", "abcdabcd") == True) assert(isAnagram("abcdaBcD", "AAbbcddc") == True) assert(isAnagram("abcdaabcd", "aabbcddcb") == False) print("Passed!") testIsAnagram()
      Note: while collections is not part of the 112 required curriculum, it is an interesting and sometimes very helpful module. For more, see here.

    • mostFrequent(L)
      def mostFrequent(L): # Return most frequent element in L, resolving ties arbitrarily. maxValue = None maxCount = 0 counts = dict() for element in L: count = 1 + counts.get(element, 0) counts[element] = count if (count > maxCount): maxCount = count maxValue = element return maxValue def testMostFrequent(): print("Testing mostFrequent()... ", end="") assert(mostFrequent([2,5,3,4,6,4,2,4,5]) == 4) assert(mostFrequent([2,3,4,3,5,3,6,3,7]) == 3) assert(mostFrequent([42]) == 42) assert(mostFrequent([]) == None) print("Passed!") testMostFrequent()

    • mostPopularNames()
      # mostPopularNames.py # example creating a dictionary # from real data on the web # This data is copied from: # https://www.ssa.gov/oact/babynames/decades/century.html mostPopularNamesData = '''\ 1 James 4,735,694 Mary 3,265,105 2 John 4,502,387 Patricia 1,560,897 3 Robert 4,499,901 Jennifer 1,467,664 4 Michael 4,330,025 Linda 1,448,309 5 William 3,601,719 Elizabeth 1,428,981 6 David 3,563,170 Barbara 1,402,428 7 Richard 2,467,544 Susan 1,104,407 8 Joseph 2,352,889 Jessica 1,045,519 9 Thomas 2,160,330 Sarah 993,847 10 Charles 2,106,078 Karen 985,728 11 Christopher 2,032,843 Nancy 969,544 12 Daniel 1,889,640 Lisa 965,003 13 Matthew 1,600,285 Margaret 944,344 14 Anthony 1,403,920 Betty 938,638 15 Donald 1,348,220 Sandra 873,609 16 Mark 1,346,509 Ashley 847,504 17 Paul 1,286,846 Dorothy 847,468 18 Steven 1,281,302 Kimberly 838,235 19 Andrew 1,252,016 Emily 826,262 20 Kenneth 1,226,558 Donna 823,285 21 Joshua 1,214,872 Michelle 811,401 22 Kevin 1,172,372 Carol 807,303 23 Brian 1,166,797 Amanda 772,882 24 George 1,159,331 Melissa 753,157 25 Edward 1,097,742 Deborah 739,809 26 Ronald 1,073,062 Stephanie 738,123 27 Timothy 1,069,165 Rebecca 729,683 28 Jason 1,035,285 Laura 721,299 29 Jeffrey 975,104 Sharon 720,816 30 Ryan 937,629 Cynthia 705,685 31 Jacob 925,412 Kathleen 689,366 32 Gary 899,858 Amy 680,682 33 Nicholas 891,818 Shirley 668,154 34 Eric 877,492 Angela 658,437 35 Jonathan 844,121 Helen 652,923 36 Stephen 840,005 Anna 629,400 37 Larry 802,430 Brenda 606,286 38 Justin 777,285 Pamela 592,694 39 Scott 769,663 Nicole 588,265 40 Brandon 759,155 Samantha 576,029 41 Benjamin 730,425 Katherine 574,858 42 Samuel 710,086 Emma 570,150 43 Frank 707,244 Ruth 563,391 44 Gregory 706,987 Christine 563,333 45 Raymond 679,913 Catherine 550,466 46 Alexander 666,982 Debra 548,279 47 Patrick 663,725 Rachel 546,309 48 Jack 637,347 Carolyn 542,250 49 Dennis 611,319 Janet 541,277 50 Jerry 602,696 Virginia 531,894 51 Tyler 589,687 Maria 528,760 52 Aaron 579,578 Heather 524,161 53 Jose 559,823 Diane 515,256 54 Henry 553,392 Julie 506,315 55 Adam 551,342 Joyce 500,601 56 Douglas 549,324 Victoria 481,786 57 Nathan 544,555 Kelly 471,257 58 Peter 540,603 Christina 471,012 59 Zachary 537,934 Lauren 469,625 60 Kyle 480,276 Joan 469,101 61 Walter 476,581 Evelyn 466,314 62 Harold 448,640 Olivia 464,246 63 Jeremy 437,552 Judith 449,885 64 Ethan 435,390 Megan 437,186 65 Carl 431,805 Cheryl 436,878 66 Keith 431,764 Martha 434,595 67 Roger 429,723 Andrea 434,410 68 Gerald 428,208 Frances 429,429 69 Christian 425,034 Hannah 426,616 70 Terry 422,106 Jacqueline 420,348 71 Sean 418,691 Ann 412,411 72 Arthur 415,722 Gloria 409,072 73 Austin 411,665 Jean 407,127 74 Noah 409,039 Kathryn 406,120 75 Lawrence 407,197 Alice 404,664 76 Jesse 388,484 Teresa 404,603 77 Joe 388,230 Sara 401,653 78 Bryan 381,581 Janice 400,210 79 Billy 379,560 Doris 395,048 80 Jordan 378,141 Madison 387,071 81 Albert 377,227 Julia 383,225 82 Dylan 377,049 Grace 379,239 83 Bruce 375,986 Judy 378,014 84 Willie 367,775 Abigail 373,862 85 Gabriel 352,072 Marie 373,633 86 Alan 348,591 Denise 371,019 87 Juan 345,375 Beverly 370,608 88 Logan 341,413 Amber 369,981 89 Wayne 339,139 Theresa 369,848 90 Ralph 338,689 Marilyn 369,847 91 Roy 338,079 Danielle 367,791 92 Eugene 330,306 Diana 358,617 93 Randy 327,821 Brittany 358,579 94 Vincent 322,824 Natalie 352,644 95 Russell 321,274 Sophia 351,883 96 Louis 320,157 Rose 351,296 97 Philip 315,423 Isabella 342,345 98 Bobby 313,302 Alexis 339,548 99 Johnny 308,243 Kayla 339,169 100 Bradley 306,339 Charlotte 338,315''' # remove the commas in the numbers: mostPopularNamesData = mostPopularNamesData.replace(',','') def mostPopularNames(): mostPopularNameCounts = dict() for line in mostPopularNamesData.splitlines(): index,name1,count1,name2,count2 = line.split() mostPopularNameCounts[name1] = int(count1) mostPopularNameCounts[name2] = int(count2) while True: name = input('Enter a name (or leave blank to quit) --> ') name = name.capitalize() if (name == ''): break if (name in mostPopularNameCounts): print(f' {name} has a count of {mostPopularNameCounts[name]}') else: print(f' {name} is not in the list!') mostPopularNames()

    • simpleTextAdventure() (optional)
      # simple-text-adventure.py # This is designed to be very simple, and to showcase # dictionaries and sets. In fact, it uses dictionaries # in some ways that would be better-designed using # OOP (object-oriented programming). We'll revisit this # example and improve upon it when we learn OOP later # this semester. # To win the (rather silly) game: # get orange # go east # feed statue def simpleTextAdventure(): gameState = getInitialGameState() while not gameState['gameOver']: currentRoom = getCurrentRoom(gameState) print(f'I am in {currentRoom["description"]}') print(f'I am carrying {thingsToString(gameState["inventory"])}') print(f'I can see {thingsToString(currentRoom["contents"])}') verb, noun = getCommand() if (verb == 'go'): doGo(gameState, noun) elif (verb == 'quit'): print('Goodbye!'); break elif (verb == 'get'): doGet(gameState, noun) elif (verb in ['put', 'drop']): doPut(gameState, noun) elif (verb == 'feed'): doFeed(gameState, noun) def thingsToString(things): things = sorted(things) if (len(things) == 0): return 'nothing' elif (len(things) == 1): return thingToString(things[0]) else: result = '' for i in range(len(things)): if (i > 0): if (i == len(things)-1): if (len(things) == 2): result += ' and ' else: result += ', and ' else: result += ', ' result += thingToString(things[i]) return result def thingToString(thing): if (thing[-1] == 's'): return thing elif (thing[0] in 'aeiou'): return 'an ' + thing else: return 'a ' + thing def doGet(gameState, noun): currentRoom = getCurrentRoom(gameState) inventory = gameState['inventory'] contents = currentRoom['contents'] if (noun in contents): contents.remove(noun) inventory.add(noun) else: print('I cannot see that here.') def doFeed(gameState, noun): currentRoom = getCurrentRoom(gameState) inventory = gameState['inventory'] contents = currentRoom['contents'] if (noun != 'statue'): print('I can only feed statues!') elif ('statue' not in contents): print('I do not see the statue here!') elif ('orange' not in inventory): print('I need to have food to feed it (like an orange)') else: inventory.remove('orange') print('I fed the statue, and it came alive, and said:') print('YOU WIN!!!!!!!') gameState['gameOver'] = True def doPut(gameState, noun): currentRoom = getCurrentRoom(gameState) inventory = gameState['inventory'] contents = currentRoom['contents'] if (noun in inventory): inventory.remove(noun) contents.add(noun) else: print('I am not carrying that.') def doGo(gameState, direction): currentRoom = getCurrentRoom(gameState) exits = currentRoom['exits'] if (direction in exits): gameState['currentRoomName'] = exits[direction] else: print(f"I can't go {direction} from here!") def getCurrentRoom(gameState): name = gameState['currentRoomName'] return gameState[name] def getCommand(): while True: response = input('Your command --> ') print() if (response == 'help'): print('No help yet!') elif (response in ['quit', 'exit', 'bye']): return 'quit','now' else: words = response.split() if (len(words) != 2): print('Please enter commands in the form VERB NOUN') print('Or just enter "help" for more help.') else: return words def getInitialGameState(): return { 'gameOver': False, 'currentRoomName' : 'kitchen', 'inventory': { 'whistle' }, 'kitchen': { 'description':'a bright and airy kitchen', 'exits': { 'north':'study', 'east':'porch' }, 'contents': { 'plates', 'cup', 'orange' }, }, 'study': { 'description':'a study full of piles and piles of books', 'exits': { 'south':'kitchen' }, 'contents': { 'thesaurus', 'dictionary', }, }, 'porch': { 'description':'a huge covered porch with lots of chairs', 'exits': { 'west':'kitchen' }, 'contents': { 'statue' }, }, } simpleTextAdventure()