Data structures are fundamental constructs in Python that are used to store and organize data. Some of the most common basic data structures in Python include lists, dictionaries, and sets. These data structures can be used to solve a wide variety of problems, and they are essential for efficient programming.

3.1: Lists

List: a mutable data structure that stores elements in an unordered format, like an array.

# Initiate an empty list
list1 = []
# OR
list1 = list()

# Initiate a list with elements
list2 = ['hello', 'hola', 'olá'] # Notice the square brackets.

# Elements in list does NOT have to be the same type (but this is uncommon).
# In this case, each list could represent the series of information about a person, but you will need to remember what information is stored at each index. 
# ---> There is a better option for this purpose - dictionary.
list3 = ["Robert", "male", 21, False, True]

# Accessing information stored in the list by position ("index")
# Note: in python, first position is ALWAYS 0, but in R is 1.
print("First element in list2 : "+ list2[0])
## First element in list2 : hello
print("Second element in list2 : "+ list2[1])

# Insert a new element as a specific location, at index 1
## Second element in list2 : hola
list2.insert(1,'hallo')
list2

# Append a new element at the END of the list
## ['hello', 'hallo', 'hola', 'olá']
list2.append('ciao')
list2

# Remove an element from the list by specifying the element that you want to remove
## ['hello', 'hallo', 'hola', 'olá', 'ciao']
list2.remove('hello')
list2
# list2 after 'hello' is REMOVED
## ['hallo', 'hola', 'olá', 'ciao']
list2
## ['hallo', 'hola', 'olá', 'ciao']

Lists could also be sorted. Method of sorting depends on how the comparable interface is implemented for the objects in the list.

list2.sort()
list2
# In this case of list2, sort() works by sorting individual characters in the string according to the ASCII code.
## ['ciao', 'hallo', 'hola', 'olá']

Since list is dynamic, meaning that the size of the list grow or shrink as we insert or remove elements, we could call len() to find the size of the list at a given time.

# Since len() returns an int, in order to concatenate it to a string, we need to transform it. 
print("size of list1 = " + str(len(list1)))
## size of list1 = 0
print("size of list2 = " + str(len(list2)))

# Print items in list as a string, separated by a comma
## size of list2 = 4
",".join(list2)
## 'ciao,hallo,hola,olá'

You could also have a list of lists. For example:

lists = []
lists.append([1,2,3])
lists.append(['a','b','c'])
lists

# Similarly, you could index the over multi-dimensional lists
## [[1, 2, 3], ['a', 'b', 'c']]
lists[1]
## ['a', 'b', 'c']
lists[1][0]
## 'a'

3.2: Tuple

Tuple is an immutable “list” which cannot be manipulated once created.They support all the operations lists supports, except for those that modify the list.

# Initialize an empty tuple
y = tuple()
y

# Create a new tuple of elements
## ()
x = (1,2,3) # Notice the parenthesis
x.index(1) # For tuples first position is 1
## 0
x.index(0)
x.append(4)
# Convert a tuple into a list
x = (1,2,3,4)
list(x)
## [1, 2, 3, 4]
x

# Convert a list into a tuple
## (1, 2, 3, 4)
x = [1,2,3,4]
tuple(x)
## (1, 2, 3, 4)
x
## [1, 2, 3, 4]
# Declare a new tuple, name "person"
person  = ('Jane','Doe', 21)

# "Pack"/associate each element of the tuple with a label. Note the order of labels.
first, last, age = person

3.3: Set

Set is a mutable data structure that stores non-duplicated, immutable objects and sorts the elements in ascending order. Every element in the set is unique.

# Initialize an empty set
newSet = set()
newSet

# A set with elements
## set()
set1 = {1, 2, 2, 1, 1}

# A set with j elements
set2 = {j for j in range(10)}
set2
## {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
set2.add(100)
set2.add(50)
set2
## {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 100, 50}
# mutable objects can't go in a set
x_set = {[1,2,3]}
## Error: TypeError: unhashable type: 'list'
x_set
## Error: NameError: name 'x_set' is not defined
# Convert a list to a set
numbers = [10, 5, 4, 5, 2, 1, 5]
set_of_numbers = set(numbers)
set_of_numbers

# Convert a set to a list
## {1, 2, 4, 5, 10}
list_of_numbers = list(set_of_numbers)
list_of_numbers

# Convert a set to a tuple
## [1, 2, 4, 5, 10]
tuple_of_numbers = tuple(list_of_numbers)
tuple_of_numbers
## (1, 2, 4, 5, 10)
# Order is irrelevant in set comparison since elements are sorted
{100,999,22,0} == {0, 22,100,999}
## True

3.4: Dictionary

Dictionary is a data structure that stores key-value pairs in which the keys MUST be immutable objects.

# Initiate an empty dictionary
# Same as set, but with :
dict = {}

# Declare a dictionary with key/value pairs
dict2 = {'a': 5, 'b': 10, 'c': 100, 'd': 9.5}

# Accessing data in a dictionary with a key
dict2['b']

# Update value of an existing key
## 10
dict2['b'] = 50
dict2['b']

# Adding a new value to our dictionary
## 50
dict2['z'] = 999
dict2['z']
## 999
# What happen if we want to access the value for a non-existing key? (e.g. 'z')
dict2['r']
## Error: KeyError: 'r'
#  Values in the dictionary can be mix-typed
# Let's look at an example with dict{}, an empty dictionary initiated above.
# Fist, we will insert some key/value pairs into the program. 

dict["greeting"] = "oi"
dict["alphabet"] = ['a', 'b', 'c', 'd', 'e']
dict["check-in"] = (False, True, False)
dict["phoneNumber"] = 8007782346

dict
## {'greeting': 'oi', 'alphabet': ['a', 'b', 'c', 'd', 'e'], 'check-in': (False, True, False), 'phoneNumber': 8007782346}
# We could also retrieve all the keys
dict.keys()

# Or all values
## dict_keys(['greeting', 'alphabet', 'check-in', 'phoneNumber'])
dict.values()

# Elements in a dictionary could also be returned as a pair.
## dict_values(['oi', ['a', 'b', 'c', 'd', 'e'], (False, True, False), 8007782346])
dict.items()
## dict_items([('greeting', 'oi'), ('alphabet', ['a', 'b', 'c', 'd', 'e']), ('check-in', (False, True, False)), ('phoneNumber', 8007782346)])