Exploring Lists and Tuples: Further Samples

In my previous post, I gave samples of working with lists and tuples in Python. Here I’ll provide more complex examples of them. But first, starting by comparing strings, lists and tuples can be a good recap.

Compare strings, lists, and tuples

Some of Python’s core iterable sequence data structures are strings, lists, and tuples. These structures share many similarities, but there are some key differences between them. It would be a nice practice to compare them here.

Content

Strings
Strings can contain any character (letters, numbers, punctuation marks, spaces) but everything between the opening and closing quotation marks is part of the same single string.

Lists
Lists can contain any data type, and in any combination. 
So, a single list can contain strings, integers, floats, tuples, dictionaries, and other lists.

Tuples
Tuples can contain any data type, and in any combination. 
So, a single tuple can contain strings, integers, floats, lists, dictionaries, and other tuples.

Mutability

Strings
Strings are immutable. Once a string is created, it cannot be modified. Any operation that appears to modify a string actually creates a new string object.

Lists
Lists are mutable. This means that they can be modified after they are created.

Tuples
Tuples are immutable. This means that once a tuple is created, it cannot be modified.

Usage

Strings
Strings are most commonly used to represent text data.

Lists
Lists are very versatile and therefore are used in numerous cases. Some common ones are:

  • Storing collections of related items.
  • Storing collections of items that we want to iterate over: Because lists are ordered, we can easily iterate over their elements using a for loop or list comprehension.
  • Sorting and searching: Lists can be sorted and searched, making them useful for tasks such as finding the minimum or maximum value in a list or sorting a list of items alphabetically.
  • Modifying existing data: Because lists are mutable, they are useful for situations in which we know we’ll need to modify our data.
  • Storing results: Lists can be used to store the results of a computation or a series of operations, making them useful in many different programming tasks.

Tuples
Common uses of tuples include:

  • Returning multiple values from a function.
  • Packing and unpacking sequences: We can use tuples to assign multiple values in a single line of code.
  • Dictionary keys: Because tuples are immutable, they can be used as dictionary keys, whereas lists cannot.
  • Data integrity: Due to their immutable nature, tuples are a more secure way of storing data because they safeguard against accidental changes.
Methods

Strings
The Python string class comes packed with many useful methods to manipulate the data contained in strings. 
For more information on these methods, refer to Common String Operations in the Python documentation.

Lists
Methods for the Python list class can be found in More on Lists in the Python documentation.

Tuples
Because tuples are built for data security, Python has only two methods that can be used on them:
count() returns the number of times a specified value occurs in the tuple.
index() searches the tuple for a specified value and returns the index of the first occurrence of the value.

More with loops, lists, and tuples

Sample 1: Now let’s check more complex examples of loops, lists, and tuples.

# Create a list of tuples, each representing the name, age, and position
# of a player on a basketball team.

team = [
    ('Marta', 20, 'center'),
    ('Ana', 22, 'point guard'),
    ('Gabi', 22, 'shooting guard'),
    ('Luz', 21, 'power forward'),
    ('Lorena', 19, 'small forward'),
    ]
# Create a function to extract names and positions from the team list
# and format them to be printed. Returns a list.

def player_position(players):
    result = []
    for name, age, position in players:
        result.append('Name: {:>19} \nPosition: {:>15}\n'.format(name, position))
    return result

# Loop over the list of formatted names and positions produced 
# by player_position() function and print them.

for player in player_position(team):
    print(player)
Name:               Marta
Position: center

Name: Ana
Position: point guard

Name: Gabi
Position: shooting guard

Name: Luz
Position: power forward

Name: Lorena
Position: small forward 

Sample 2: Let’s create dominos with nested loops.

# Nested loops can produce the different combinations of pips (dots)
# in a set of dominoes.

for left in range(7):
    for right in range(left, 7):
        print(f"[{left}|{right}]", end=" ")
    print('\n')
[0|0] [0|1] [0|2] [0|3] [0|4] [0|5] [0|6]
 
[1|1] [1|2] [1|3] [1|4] [1|5] [1|6]

[2|2] [2|3] [2|4] [2|5] [2|6]

[3|3] [3|4] [3|5] [3|6]

[4|4] [4|5] [4|6]

[5|5] [5|6]

[6|6]

If we wouldn’t add the ‘end’ part, all the dominoes would have been printed in a vertical line, each one beneath the next.

We could create the dominos as tuples as well.

# Create a list of dominoes, with each domino represented as a tuple.
dominoes = []

for left in range(7):
    for right in range(left, 7):
        dominoes.append((left, right))
dominoes
[(0, 0),
(0, 1),
(0, 2),
(0, 3),
(0, 4),
(0, 5),
(0, 6),
(1, 1),
(1, 2),
(1, 3),
(1, 4),
(1, 5),
(1, 6),
(2, 2),
(2, 3),
(2, 4),
(2, 5),
(2, 6),
(3, 3),
(3, 4),
(3, 5),
(3, 6),
(4, 4),
(4, 5),
(4, 6),
(5, 5),
(5, 6),
(6, 6)]
# Select index 1 of the tuple at index 4 in the list of dominoes.
dominoes[4][1]
4

Some time saving tools for iterable objects

Iterable objects such as strings, lists, and tuples comprise many of Python’s core data structures and data professionals work with them constantly. While working in Python, we’ll often need to perform the same tasks and operations many times. 

Here are three time-saving tools: zip(), enumerate(), and list comprehension.

zip()

The function returns an iterator that produces tuples containing elements from each of the input sequences. An iterator is an object that enables processing of a collection of items one at a time without needing to assemble the entire collection at once. Use an iterator with loops or other iterable functions such as list() or tuple().

cities = ['Paris', 'Lagos', 'Mumbai']
countries = ['France', 'Nigeria', 'India']

places = zip(cities, countries)
print(places)
print(list(places))
<zip object at 0x7f61f808c948>
[('Paris', 'France'), ('Lagos', 'Nigeria'), ('Mumbai', 'India')]

● It works with two or more iterable objects. The given example zips two sequences, but the zip() function will accept more sequences and apply the same logic.
● If the input objects are of unequal length, the resulting iterator will be the same length as the shortest input.
● If we give it only one iterable object as an argument, the function will return an iterator that produces tuples containing only one element from that iterable at a time.

Unzipping

We can also unzip an object with the * operator.

scientists = [('Nikola', 'Tesla'), ('Charles', 'Darwin'), ('Marie', 'Curie')]
given_names, surnames = zip(*scientists)
print(given_names)
print(surnames)
('Nikola', 'Charles', 'Marie')
('Tesla', 'Darwin', 'Curie')

enumerate() 

The enumerate() function is another built-in Python function that allows us to iterate over a sequence while keeping track of each element’s index. Similar to zip(), it returns an iterator that produces pairs of indices and elements.

letters = ['a', 'b', 'c']
for index, letter in enumerate(letters):
   print(index, letter)
0 a
1 b
2 c

We can also assign the starting index.

letters = ['a', 'b', 'c']
for index, letter in enumerate(letters, 2):
   print(index, letter)
2 a
3 b
4 c

The enumerate() function is useful when an element’s place in a sequence must be used to determine how the element should be handled in an operation.

List comprehension

One of the most useful tools in Python is list comprehension. List comprehension is a concise and efficient way to create a new list based on the values in an existing iterable object. List comprehensions take the following form:

my_list = [expression for element in iterable if condition]

Let’s get back to the dominos that we created earlier.

If we’d like to add the total number of pips on each individual domino to a list, we could use one of the following two ways. The first way uses a for loop, the second one uses a list comprehension.

# We can use a for loop to sum the pips on each domino 
# and append the sum to a new list.

pips_from_loop = []
for domino in dominoes:
    pips_from_loop.append(domino[0] + domino[1])
print(pips_from_loop)
[0, 1, 2, 3, 4, 5, 6, 2, 3, 4, 5, 6, 7, 4, 5, 6, 7, 8, 6, 7, 8, 9, 8, 9, 10, 10, 11, 12]
# A list comprehension produces the same result with less code.

pips_from_list_comp = [domino[0] + domino[1] for domino in dominoes]
pips_from_loop == pips_from_list_comp
True

A list comprehension is like a for loop written in reverse. The “for” part of it is at the end of the statement and the computation is at the beginning. Both the for loop and the list comprehension do the same thing, but the list comprehension is much more elegant, and usually faster to execute too.

Another sample would be the below list comprehension that adds 10 to each number in the list:

numbers = [1, 2, 3, 4, 5]
new_list = [x + 10 for x in numbers]
print(new_list)
[11, 12, 13, 14, 15]

Or we can take advantage of indexing and conditional statements too.

words = ['Emotan', 'Amina', 'Ibeno', 'Sankwala']
new_list = [(word[0], word[-1]) for word in words if len(word) > 5]
print(new_list)
[('E', 'n'), ('S', 'a')]

More samples

Let’s practice more. We’ll start with creating lists of states and counties.

Create lists

state_names = ["Arizona", "California", "California", "Kentucky", "Louisiana"]
county_names = ["Maricopa", "Alameda", "Sacramento", "Jefferson", "East Baton Rouge"]

Use a loop to combine the lists into a single list of tuples

We’ll create a new list of tuples, where each tuple contains a pair of state name and county name.

state_county_tuples = []
for i in range(len(state_names)):
    state_county_tuples.append((state_names[i], county_names[i]))
state_county_tuples
[('Arizona', 'Maricopa'),
('California', 'Alameda'),
('California', 'Sacramento'),
('Kentucky', 'Jefferson'),
('Louisiana', 'East Baton Rouge')]

We can do the same thing using the zip() function

As seen above, the zip function can help us on this one.

state_county_zipped = list(zip(state_names, county_names))
state_county_zipped == state_county_tuples
True

Use list comprehension to convert to list of lists

Since tuples are immutable and can’t be changed, converting tuples to lists is a practice data professionals use so they can make adjustments to the data, if necessary.

state_county_lists = [list(i) for i in state_county_tuples]
state_county_lists
[['Arizona', 'Maricopa'],
['California', 'Alameda'],
['California', 'Sacramento'],
['Kentucky', 'Jefferson'],
['Louisiana', 'East Baton Rouge']]

Unpacking in a loop

The Technique of unpacking is useful to work with individual elements of iterable objects. Imagine that we were asked to iterate through our list of state/county pairs to identify only the counties in California and add the corresponding county to a list called ca_counties.

As a refresher, here is the data we have been working with:

state_namecounty_name
ArizonaMaricopa
CaliforniaAlameda
CaliforniaSacramento
KentuckyJefferson
LouisianaEast Baton Rouge
ca_counties = []
for state, county in state_county_tuples:
    if state=='California':
        ca_counties.append(county)
ca_counties
['Alameda', 'Sacramento']

Unpacking in a list comprehension

Let’s do the same thing by using a list comprehension.

ca_counties = [county for (state, county) in state_county_tuples if state=='California']
ca_counties
['Alameda', 'Sacramento']

As a summary:

  • Lists and tuples are important iterable data types in Python that share many characteristics.
  • Tuples in Python are useful for storing data in ordered sequences that are preserved and cannot be modified after creation.
  • The zip() function is very useful for combining iterable objects element-wise.
  • Tuples and lists can be unpacked.
  • List comprehensions are quick and efficient ways to execute loop-like processes in a single line of code that results in a list.