Dictionaries

Topics Covered : The Dictionary Data Type, Pretty Printing, Using Data Structures to Model Real-World Things

A dictionary is a collection of items that are unordered, changeable, and indexed. Dictionary items are made up of Key – Value pair and each key is separated from its value by a colon (: ) and whole thing is enclosed in curly braces as illustrated in the following syntax:

Syntax: my_dict = {‘key1’: ‘value1′,’key2’: ‘value2′,’key3′: value3’…’keyn’: ‘valuen’}

Example:

my_dict = { 
    "brand":"Maruthi",
    "Model": "Suzuki",
    "Year":2023
}   
print(my_dict)
#Output : 
{'brand': 'Maruthi', 'Model': 'Suzuki', 'Year': 2023}

Keys are unique within a dictionary while values may not be. The values of a dictionary can be of any type, but the keys must be of an immutable data type such as strings, numbers, or tuples. A dictionary is an extremely useful data storage construct for storing and retrieving all key value pairs, where each element is accessed (or indexed) by a unique key. However, dictionary keys are not in sequences and hence maintain no left-to right order.

Dictionary is a mapping between a set of indices (called keys) and a set of values. Each key maps a value. The association of a key and a value is called a key-value pair.

Creating Dictionaries:

Creating Empty Dictionary: Empty dictionaries can be created using curly braces and dict() function as illustrated below:

# Creating Empty Dictionaries 
ed1 = {}  # using curly braces
ed2 = dict() # using dict() function
print(ed1)
print(ed2)
#Output:
{}
{}

Creating Dictionary with Multiple Elements:

Dictionaries with multiple elements can be created using curly braces. Each element containing key value pair are separated by comma operator as illustrated below:

Example 1:

# Creating dictionary with multiple elements 
dict1 = {"Name":"Palguni","USN":"4MCECSBS123","Course":  "BE","Branch":"CSBS","SEM":"1"}
print(dict1)

#Output:
{'Name': 'Palguni', 'USN': '4MCECSBS123', 'Course': 'BE', 'Branch': 'CSBS', 'SEM': '1'}

Example 2 :

H=dict()
H["one"]="keyboard" 
H["two"]="Mouse" 
H["three"]="printer" 
H["Four"]="scanner" 
print(H)

#Output:

{'one': 'keyboard', 'two': 'Mouse', 'three': 'printer', 'Four': 'scanner'}

Accessing Values in Dictionary

To access dictionary elements, you can use the familiar square brackets along with the key to obtain its value. Following is a simple example:

student = {"Name":"Palguni","USN":"4MCECSBS123","Course": "BE","Branch":"CSBS","SEM":"1"}
print(student["Name"])
print(student["USN"])
print(student["Course"])
print(student["Branch"])
print(student["SEM"])

#Output 

Palguni
4MCECSBS123
BE
CSBS
1

If we attempt to access a data item with a key, which is not part of the dictionary, we get an error as follows –

print(student["Age"])
# Output:
KeyError: 'Age'

Updating Dictionary

Dictionary can be updated by adding a new entry or a key-value pair, modifying an existing entry, or deleting an existing entry as shown below in the simple example:

student = {"Name":"Raju","Class":8}
print(student)
#Output:
{'Name': 'Raju', 'Class': 8}

student["Age"] = 10
student["School"] = "Contextual International School"
print(student)
#Output:
{'Name': 'Raju', 'Class': 8, 'Age': 10, 'School': 'Contextual International School'}

Deleting Dictionary Elements

You can either remove individual dictionary elements or clear the entire contents of a dictionary.

Consider a dictionary dictx as illustrated below:

dictx = {'Name': 'Pala', 'Age': 7, 'Class': 'First'}
print(dictx)
#Output: {'Name': 'Pala', 'Age': 7, 'Class': 'First'}

Following example illustrates the deletion of single element with key ‘Name’:

del dictx['Name']; # remove entry with key 'Name'
print(dictx)
#Output: {'Age': 7, 'Class': 'First'}

The clear() method can be used to delete all the elements of dictionary as illustrated below:

dictx.clear()    # remove all entries in dict
print(dictx)
#Output: {}

One can also delete the entire dictionary in a single operation. To explicitly remove an entire dictionary, just use the del statement. Following is a simple example –

del dictx        # delete entire dictionary
print(dictx)
#Output: NameError: name 'dictx' is not defined

Properties of Dictionary Keys

Dictionary values have no restrictions, and they can be of any arbitrary object. Keys are immutable type with following properties:

  1. More than one entry per key is not allowed. When duplicate key is allowed the last assignment wins:
     dictX = {'Name':"Raju",'Age':16,"Name":"Rahul"}
     print(dictX)
     #Output: {'Name': 'Rahul', 'Age': 16}

2. Since keys are immutable, one can use strings , numbers, or tuples as dictionary keys.

Python Dictionary items() method

It returns the content of dictionary as a list of key and value. The key and value pair
will be in the form of a tuple, which is not in any order.

Syntax: dictionary. items()

D={'1':'Sunday','1':'Monday','3':'Tuesday','4':'Wednesday'} 
D.keys()
#Output:
dict_keys(['1', '3', '4'])

list(D.keys())
#Output:
['1', '3', '4']

Python Dictionary Values Method

It returns a list of values from key-value pairs in a dictionary, which is not in any order. However, if we call both the items() and values() method without changing the dictionary’s contents between these two (items() and values()), Python guarantees that the order of the two results will be the same.

D = {'1':'Rohan','2':'Raju','3':'Pallu','4':'Pallavi','5':'Deeksha'}
D.values()

#Output:
dict_values(['Rohan', 'Raju', 'Pallu', 'Pallavi', 'Deeksha'])

list(D.values())
#Output:
['Rohan', 'Raju', 'Pallu', 'Pallavi', 'Deeksha']

Traversing Dictionary elements using for loop.

In Python, you can traverse the elements of a dictionary using various methods. Here are a few common ways to iterate over dictionary elements.

Example 1: Traversing Dictionary using keys

D = {'1':'Rohan','2':'Raju','3':'Pallu','4':'Pallavi','5':'Deeksha'}
for i in D.keys():
    print(i,":",D[i])

#Output:
1 : Rohan
2 : Raju
3 : Pallu
4 : Pallavi
5 : Deeksha

Example 2 : Traversing Dictionary using values

D = {'1':'Rohan','2':'Raju','3':'Pallu','4':'Pallavi','5':'Deeksha'}
for i in D.values():
    print(i)

#Output:
Rohan
Raju
Pallu
Pallavi
Deeksha

Example 3 : Traversing Dictionary using values

D = {'1':'Rohan','2':'Raju','3':'Pallu','4':'Pallavi','5':'Deeksha'}
for i in D.items():
    print(i)

#Output:
('1', 'Rohan')
('2', 'Raju')
('3', 'Pallu')
('4', 'Pallavi')
('5', 'Deeksha')

Membership Operators with Dictionary

Membership operators (in and not in) can be used to check for the presence or absence of keys in a dictionary. Here’s how you can use membership operators with dictionaries in Python:

Example 1: Checking whether a key or value exists in a dictionary

D = {'1':'Rohan','2':'Raju','3':'Pallu','4':'Pallavi','5':'Deeksha'}
"10" in D.keys()
#Output: False

D = {'1':'Rohan','2':'Raju','3':'Pallu','4':'Pallavi','5':'Deeksha'}
"2" in D.keys()
#Output:  True

D = {'1':'Rohan','2':'Raju','3':'Pallu','4':'Pallavi','5':'Deeksha'}
"Pallu" in D.values()
#Output: True

Example 2: Check if a key does not exist in the dictionary

if “key4” not in my_dict:
print(“Key ‘key4’ does not exist in the dictionary.”)

To compare dictionaries in Python, you can use the equality operator (==) or the cmp() function. Here’s how you can compare dictionaries using these methods:

  1. Using the equality operator (==):
dict1 = {"key1": "value1", "key2": "value2"}
dict2 = {"key1": "value1", "key2": "value2"}

if dict1 == dict2:
    print("The dictionaries are equal.")
else:
    print("The dictionaries are not equal.")

2. Using the cmp( ) function (available in Python 2):

dict1 = {"key1": "value1", "key2": "value2"}
dict2 = {"key1": "value1", "key2": "value2"}

if  cmp(dict1, dict2) == 0:
     print("The dictionaries are equal.")
else:
     print("The dictionaries are not equal.")

Merging dictionaries: An update ( )

Syntax:  Dic_name1.update (dic_name2)

In Python, you can merge dictionaries using the update() method. The update() method modifies the dictionary in place by adding key-value pairs from another dictionary. Here’s how you can use the update() method to merge dictionaries:

dict1 = {"key1": "value1", "key2": "value2"}
dict2 = {"key3": "value3", "key4": "value4"}
dict1.update(dict2)
print(dict1)
# Output: {"key1": "value1", "key2": "value2", "key3": "value3", "key4": "value4"}

In the example above, dict1.update(dict2) merges dict2 into dict1. The update() method adds all the key-value pairs from dict2 to dict1. If there are common keys between the dictionaries, the values from dict2 will overwrite the values in dict1 for those keys.

len ()

This method returns the number of key-value pairs in the given dictionary.

D = {'1':'Rohan','2':'Raju','3':'Pallu','4':'Pallavi','5':'Deeksha'}
len(D)
# Output: 5

get() and setdefault() methods with dictionary.

get() method:

The get() method retrieves the value associated with a given key in the dictionary. It takes the key as the first argument and an optional second argument specifying a default value to return if the key is not found in the dictionary. Consider the following example:

my_dict = {"key1": "value1", "key2": "value2"}
value1 = my_dict.get("key1")
print(value1)  				# Output: "value1"
value3 = my_dict.get("key3", "default")
print(value3)  

# Output: "default" (since "key3" doesn't exist)

In the example above, my_dict.get(“key1”) returns the value associated with “key1”, which is “value1”. If the key “key3” doesn’t exist in the dictionary, my_dict.get(“key3”, “default”) returns the default value “default”.

Another Example:

picnic_items = {'apples':5,'cups':2}
'I am bringing ' + str(picnic_items.get('cups',0)) + ' cups'
#Output: 'I am bringing 2 cups'

'I am bringing ' + str(picnic_items.get('oranges',2)) + ' oranges'
#Output:'I am bringing 2 oranges'

setdefault() method :

The setdefault() method returns the value associated with a given key in the dictionary. If the key doesn’t exist, it adds the key with a default value to the dictionary.

my_dict = {"key1": "value1", "key2": "value2"}

value1 = my_dict.setdefault("key1", "default")
print(value1)   				# Output: "value1"
value3 = my_dict.setdefault("key3", "default")
print(value3)  				# Output: "default"

print(my_dict)  
# Output: {"key1": "value1", "key2": "value2", "key3": "default"}

Example : Get the value of the “color” item, if the “color” item does not exist, insert “color” with the value “silksilver”:

car = {
  "brand": "Hundai",
  "model": "Creta",
  "year": 2020
}
x = car.setdefault("color", "silksilver")
print(x)

#Output: 
silksilver

x = car.setdefault("brand", "Maruthi")
print(x)

#Output: 
Hundai

x = car.setdefault("Price",100000)
print(car)

#Output:
{'brand': 'Hundai', 'model': 'Creta', 'year': 2020, 'color': 'silksilver', 'Price': 100000}

Converting a dictionary to a list of tuples

Dictionary have a method called items() that returns a list of tuples where each tuple is a key value pair.

D = {'1':'Rohan','3':'Raju','4':'Pallu','2':'Pallavi','5':'Deeksha'}
t = list(D.items())
print(t)

#Output:
[('1', 'Rohan'), ('3', 'Raju'), ('4', 'Pallu'), ('2', 'Pallavi'), ('5', 'Deeksha')]

t.sort()
print(t)

#Output:
[('1', 'Rohan'), ('2', 'Pallavi'), ('3', 'Raju'), ('4', 'Pallu'), ('5', 'Deeksha')]

Pretty Printing

In Python, the pprint module provides two functions for pretty-printing data structures: pprint() and pformat(). These functions are useful when you want to display complex data structures, such as dictionaries or lists, in a more readable and structured format. Here’s an explanation of pprint.pprint() and pprint.pformat() with examples:

  1. pprint() function:

The pprint() function in the pprint module prints a data structure in a formatted and more readable manner. It accepts a single argument, which can be any data structure, such as a dictionary, list, or tuple.

# printing dictionary elements using print
student_dict = {'Name':'Tyson',
                'class':'XII',
                'Address': {'Flat':1308,'Block':'A','Lane':2,'City':"Hyderbad"}}
print(student_dict)

#Output:{'Name': 'Tyson', 'class': 'XII', 'Address': {'Flat': 1308, 'Block': 'A', 'Lane': 2, 'City': 'Hyderbad'}}

# printing dictionary elements using pprint.pprint()
import pprint
pprint.pprint(student_dict)
{'Address': {'Block': 'A', 'City': 'Hyderbad', 'Flat': 1308, 'Lane': 2},
 'Name': 'Tyson',
 'class': 'XII'}

# printing dictionary elements using pprint.pprint() with width and indent
pprint.pprint(student_dict,width = 2, indent =2)

{ 'Address': { 'Block': 'A',
               'City': 'Hyderbad',
               'Flat': 1308,
               'Lane': 2},
  'Name': 'Tyson',
  'class': 'XII'}

2. pformat() function:

The pformat() function in the pprint module returns a string representation of the data structure instead of printing it directly. This can be useful if you want to store or manipulate the formatted output.

x = pprint.pformat(student_dict,indent =5)
print(x)

#Output
{    'Address': {'Block': 'A', 'City': 'Hyderbad', 'Flat': 1308, 'Lane': 2},
     'Name': 'Tyson',
     'class': 'XII'}

Using Data Structures to Model Real-World Things

1. Using Data Structures to Model Chess:

In the realm of algebraic chess notation, the squares on the chessboard are denoted by a combination of numerical and alphabetical coordinates, exemplified in the diagram below:

Image Source : [1] https://automatetheboringstuff.com/

Chess pieces are designated by specific letters: K for king, Q for queen, R for rook, B for bishop, and N for knight. Describing a move involves utilizing the letter of the piece along with the destination coordinates. A sequence of these moves portrays the events of a single turn (initiated by the white player). For example, annotation 2. Nf3 Nc6 signifies that, on the second turn of the game, the white player advanced a knight to f3 and the black player moved a knight to c6.

Algebraic chess notation serves as a standardized system for documenting chess movements. This system empowers players to articulate their moves using a blend of letters and numbers that symbolize the squares on the chessboard.

The fundamental components of algebraic chess notation encompass:

K: King

Q: Queen

R: Rook

B: Bishop

N: Knight

No abbreviation for pawns

Square Designation:

Each square on the chessboard is designated by a combination of a letter (column) and a number (row). The letters a-h represent the columns from left to right, and the numbers 1-8 represent the rows from bottom to top.

Move Representation:

A move in algebraic notation typically consists of the piece abbreviation (if applicable), the destination square, and additional symbols to indicate the move type:

Pawn moves: Only the destination square is mentioned. If it’s a capture, the source file is indicated as well.

Example: e4, exd5 (e4 pawn moves to e5, capturing d5 pawn)

Other pieces: The piece abbreviation is followed by the destination square. If multiple pieces of the same type can move to the same square, the source file or rank is mentioned to disambiguate.

Example: Nf3 (Knight moves to f3), R1e5 (Rook on the first rank moves to e5)

Special Moves:

Castling: O-O for kingside castling and O-O-O for queenside castling.

En Passant: If a pawn captures en passant, the destination square is indicated and “e.p.” is added.

Example: exd6 e.p. (e5 pawn captures d6 pawn en passant)

Check and Checkmate:

“+” is added after a move to indicate a check.

“#” is added after a move to indicate a checkmate.

Example: Qh5+ (Queen moves to h5, giving a check), Qh5# (Queen moves to h5, giving a checkmate)

Other Symbols:

=” is used to indicate pawn promotion. It is followed by the piece abbreviation to which the pawn promotes.

Example: e8=Q (Pawn on e8 promotes to a Queen)

By using algebraic chess notation, players can easily record and communicate their moves. It is also commonly used in chess literature, annotations, and online platforms for game analysis.

Using Data Structures to Model Tic-Tac-Toe

The configuration of a tic-tac-toe board resembles a sizable hash symbol (#), featuring nine compartments, each capable of holding an X, an O, or remaining unoccupied. When portraying the board utilizing a dictionary, a feasible approach involves assigning a unique string-based key to each slot, as depicted in the illustration below:

Image Source : [1] https://automatetheboringstuff.com/

Utilizing string representations, you can symbolize the contents of each slot on the board using ‘X‘, ‘O’, or ‘ ‘ (a space character). Consequently, you will need to retain nine distinct strings. To achieve this, a suitable approach is to employ a dictionary comprising these string values. For instance, the string associated with the key ‘top-R‘ could indicate the top-right corner, the string linked to ‘low-L’ could stand for the bottom-left corner, and similarly, the string correlated with ‘mid-M’ could represent the middle section, and so forth. This dictionary constitutes a data structure that embodies the layout of a tic-tac-toe board. You can save this board-as-a-dictionary within a variable named theBoard.

theBoard = {‘top-L’: ‘ ‘, ‘top-M’: ‘ ‘, ‘top-R’: ‘ ‘,

                       ‘mid-L’: ‘ ‘, ‘mid-M’: ‘ ‘, ‘mid-R’: ‘ ‘,

                       ‘low-L’: ‘ ‘, ‘low-M’: ‘ ‘, ‘low-R’: ‘ ‘

                     }

The tic-tac-toe board depicted in the illustration below is represented by the data structure stored within the variable named theBoard:

Fig : Empty Tic Tac Toe Board

Given that each key’s value within theBoard variable is a single-space string, this dictionary delineates an entirely vacant board. In a scenario where player X takes the initiative and selects the middle space as their move, you can use this dictionary to represent the resulting board configuration:

theBoard = {‘top-L’: ‘ ‘, ‘top-M’: ‘ ‘, ‘top-R’: ‘ ‘,

                   ‘mid-L’: ‘ ‘, ‘mid-M’: ‘X’, ‘mid-R’: ‘ ‘,

                     ‘low-L’: ‘ ‘, ‘low-M’: ‘ ‘, ‘low-R’: ‘ ‘}

The data structure contained within the theBoard variable now corresponds to the tic-tac-toe board illustrated in the figure below:

Fig: The First Move

An example of a board where player O emerges victorious by strategically placing Os across the top could resemble the following depiction:

theBoard = {‘top-L’: ‘O’, ‘top-M’: ‘O’, ‘top-R’: ‘O’,

                        ‘mid-L’: ‘X’, ‘mid-M’: ‘X’, ‘mid-R’: ‘ ‘,

                        ‘low-L’: ‘ ‘, ‘low-M’: ‘ ‘, ‘low-R’: ‘X’}

The data structure stored in the theBoard variable currently reflects the tic-tac-toe board illustrated in the figure below:

Fig: Player O wins

Python Program to simulate the working of Tic-Tac-Toe:

theBoard = {'top-L': ' ', 'top-M': ' ', 'top-R': ' ',
            'mid-L': ' ', 'mid-M': ' ', 'mid-R': ' ',
            'low-L': ' ', 'low-M': ' ', 'low-R': ' '}

def printBoard(board):
    print(board['top-L'] + '|' + board['top-M'] + '|' + board['top-R'])
    print('-+-+-')
    print(board['mid-L'] + '|' + board['mid-M'] + '|' + board['mid-R'])
    print('-+-+-')
    print(board['low-L'] + '|' + board['low-M'] + '|' + board['low-R'])

turn = 'X'

for i in range(9):
    printBoard(theBoard)
    print('Turn for ' + turn + '. Move on which space?')
    move = input()
    theBoard[move] = turn
    if turn == 'X':
        turn = 'O'
    else:
        turn = 'X'
printBoard(theBoard)


# OutPut : 
| |
-+-+-
| |
-+-+-
| |
Turn for X. Move on which space?
mid-M
| |
-+-+-
|X|
-+-+-
| |
Turn for O. Move on which space?
low-L
| |
-+-+-
|X|
-+-+-
O| |
--snip--
O|O|X
-+-+-
X|X|O
-+-+-
O| |X
Turn for X. Move on which space?
low-M
O|O|X
-+-+-
X|X|O
-+-+-
O|X|X

This isn’t a complete tic-tac-toe game—for instance, it doesn’t ever check whether a player has won—but it’s enough to see how data structures can be used in programs.

Nested Dictionaries and Lists

1. Nested Dictionaries: A nested dictionary is a dictionary that contains other dictionaries as its values. It allows you to create a hierarchical structure to organize and store data. Each inner dictionary can have its own set of key-value pairs. Here’s an example:

student = {

    ‘name’: ‘John’,

    ‘age’: 20,

    ‘grades’: {

        ‘math’: 90,

        ‘science’: 85,

        ‘history’: 95

    }

}

In the example above, the student dictionary has three key-value pairs. The ‘grades’ key has a value of another dictionary, which contains subject-grade pairs.

You can access the nested values by chaining the keys together. For example, to access the math grade, you can use student[‘grades’][‘math’], which will return 90.

2. Nested Lists: A nested list is a list that contains other lists as its elements. It allows you to create a multidimensional structure to store data. Each inner list can have its own set of elements. Here’s an example:

matrix = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
]

In the example above, the matrix list contains three inner lists, forming a 3×3 matrix. You can access the elements of the nested list using indexing. For example, to access the element 5, you can use matrix[1][1], which corresponds to the second row and second column. Nested lists can be useful for representing grids, matrices, or any other data structures that require multiple dimensions.

Nested dictionaries and lists can be combined to create more complex data structures. For example, you can have a list of dictionaries, where each dictionary represents an entity with multiple properties, or you can have a dictionary that contains lists as its values, allowing you to group related items together.

Remember that when working with nested data structures, you need to carefully consider the structure and access patterns to ensure efficient and correct data manipulation.