<style>
.present {
text-align: left;
}
img[alt=set_operations] {
width: 60%;
}
</style>
# Python Collections and Built-ins
## Week 17 Day 3
---
## Videos 1 (14 minutes)
Watch:
- Lists in Python (10:15)
---
### Lists
Lists are mutable, ordered collections (like arrays in JavaScript). Use square brackets to make lists.
```python=
empty = []
print(empty) # []
```
Values are separated by commas.
```python=
fruits = ["banana", "apple", "kiwi"]
print(fruits) # ["banana", "apple", "kiwi"]
```
---
### Indexing with lists
To get individual values:
- `list_name[single_index]`
```python=
fruits = ["banana", "apple", "kiwi", "mango", "tangerine"]
print(fruits[1]) # apple
print(fruits[-1]) # tangerine
```
---
### Indexing with lists (ranges)
To get a range of values:
```python=
fruits = ["banana", "apple", "kiwi", "mango", "tangerine"]
# list_name[inclusive_start:exclusive_end]
print(fruits[1:4]) # ["apple", "kiwi", "mango"]
print(fruits[-1:]) # ["tangerine"]
# list_name[inclusive_start:exclusive_end:step_size]
print(fruits[::-1]) # ["tangerine', "mango', "kiwi", "apple", "banana"]
print(fruits[3::-1]) # ["mango", "kiwi", "apple", "banana"]
```
---
### Built-in functions (`len()`)
We can use the `len()` function to get the length of lists.
```python=
fruits = ["banana", "apple", "kiwi", "mango", "tangerine"]
print(len(fruits)) # 5
print(len(fruits[1:4])) # 3
```
---
## Practices 1 (20 min)
- Explore The List (5:00)
- Return First Element Of A List (2:00)
- First And Last Entries (1:00)
- Check Nested Arrays (10:00)
---
## Lecture Videos 2 (7 minutes)
Watch:
- Doing Math With Lists (5:07)
Discuss code only:
- Mutability Of and Sorting Lists (12:51)
---
### List methods
Use `append` to add items to the end of a list (like `push` in JavaScript). This mutates the list.
```python=
fruits = ["banana", "apple", "kiwi"]
fruits.append("apple")
print(fruits) # ["banana", "apple", "kiwi", "apple"]
```
Use `remove` to delete the first instance of a given value. (Also mutates the list).
```python=
fruits.remove("apple")
print(fruits) # ["banana", "kiwi", "apple"]
# Error if value is not present in list
fruits.remove("mango") # ValueError: list.remove(x): x not in list
```
(for more list methods, check out [the documentation here](https://docs.python.org/3/tutorial/datastructures.html).)
---
### Sorting lists
The `.sort()` method will sort a list, mutating the list
```python=
fruits = ["mango", "banana", "apple", "kiwi"]
fruits.sort()
print(fruits) # ["apple", "banana", "kiwi", "mango"]
```
---
### Sorting lists (custom sort)
You can provide a key to change how the list is sorted
- the key is a function
- rather than sort the list values directly, the sort will apply the key function to each value and sort based on the resulting value
- this will not mutate the values in the list (just the order), it will just use the calculated values in the comparisons
```python=
names = ["janie", "Julie", "Allie", "pamela"]
names.sort()
print(names) # ["Allie", "Julie", "janie", "pamela"]
# ensure a case-insensitive sort with the `.lower` string method
names.sort(key=str.lower)
print(names) # ["Allie", "janie", "Julie", "pamela"]
```
---
### Sorting, continued
The `sorted()` function can be used to sort lists without mutating. It also works with other iterable types (e.g. tuples).
```python=
fruits = ["mango", "banana", "apple", "kiwi"]
sorted_fruits = sorted(fruits)
reversed_fruits = sorted(fruits, reverse=True)
print(sorted_fruits) # ['apple', 'banana', 'kiwi', 'mango']
print(reversed_fruits) # ['mango', 'kiwi', 'banana', 'apple']
```
- note that the parameters `key` and `reverse` work with both `.sort()` and `sorted()`
---
### Sorting lists (fun fact)
Sorting in Python uses [Timsort](https://en.wikipedia.org/wiki/Timsort). (The inventor's name is Tim. Really.)
It is based on a combination of merge sort and insertion sort, and it is designed to perform optimally on real data.
---
### More built-in functions
`sum()`, `min()`, `max()`, can be used to compute values over lists.
```python=
values = [49, 22, 13, 25]
print(values) # [49, 22, 13, 25]
total_value = sum(values)
print(total_value) # 109
top_value = max(values)
print(top_value) # 49
min_value = min(values)
print(min_value) # 13
avg_val = sum(values)/len(values)
print(avg_val) # 27.25
```
---
## Practices 2 (15 min)
- Find The Smallest Number In A List (5:00)
- Sum The Elements Of A List (5:00)
- Maximum Difference (5:00)
---
## Lecture Videos 3 (22 minutes)
Watch:
- Tuples in Python (17:07)
- Special Case: Single Item Tuple (2:46)
---
### Tuples
- Tuples are an ordered, immutable collection type.
- They are defined using parentheses `()`, with values separated by a comma.
```python=
a = (1, 2, 3, 4, 5, 6, 7, 8, 9)
b = ('a', 'b', 'c', 'd', 'e')
c = 10, 20, 30
print(a) # (1, 2, 3, 4, 5, 6, 7, 8, 9)
print(b) # ('a', 'b', 'c', 'd', 'e')
print(c) # (10, 20, 30)
```
---
### Tuples are immutable [1/2]
- You cannot append, remove, or sort the tuple in place.
- However, they are not constants.
```python=
tup = ("tall", "wide")
tup = ("small", "narrow") # no error, this works
print(tup) # ("small", "narrow")
tup += ("tall", "wide") # no error, this works
print(tup) # ("small", "narrow", "tall", "wide")
```
---
### Tuples are immutable [2/2]
Mutable items in a tuple can be mutated:
```python=
tup = ([1, 2, 3], "hello")
print(tup[0]) # [1, 2, 3]
tup[0].append(4)
print(tup[0]) # [1, 2, 3, 4]
print(tup) # ([1, 2, 3, 4], "hello")
```
---
### Sorting
The `sorted()` function works on tuples, just like lists. This will return a list by default.
```python=
fruits = ("banana", "apple", "kiwi")
print(sorted(fruits)) # ['apple', 'banana', 'kiwi']
sorted_fruits = tuple(sorted(fruits))
print(sorted_fruits)
print(fruits)
```
---
### Returning tuples
- Using a comma in your return statement will return a tuple
- You can store the tuple in one variable or destructure the tuples values into multiple variables.
```python=
def min_max(numbers):
return min(numbers), max(numbers)
values = (14, 2, -2, 3.3, -8, -25, 9, 0)
low_and_high = min_max(values)
print(low_and_high) # (-25, 14)
lowest, highest = min_max(values)
print(lowest) # -25
print(highest) # 14
```
---
### Singleton tuples
Since parentheses are also used for grouping, python won't interpret a single like you might expect.
```python=
hopefully_a_tuple = (5)
print(hopefully_a_tuple) # 5
# whoops! not a tuple
```
You need to use a comma so that python can recognize that a tuple is present.
```python=
hopefully_a_tuple = (5,)
print(hopefully_a_tuple) # (5,)
# nice, that is a tuple
also_a_tuple= 5,
print(also_a_tuple) # (5,)
# awesome, that is also a tuple
```
---
## Practices 3 (5 min)
- Explore The Tuple (5:00)
---
## Lecture Videos 4 (10 minutes)
Watch:
- Ranges in Python (9:00)
---
### Ranges
- An immutable list of numbers in order
- Arguments:
- start (default 0)
- stop (required)
- step (default 1)
- Can go in forward or reverse order
- Range is exclusive(the stop argument is not included in the returned list)
---
### Declaring Basic Ranges
```python=
numbers_range = range(10)
print(numbers_range) # range(0, 10)
print(list(numbers_range)) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
starting_at_one = range(1, 11)
print(list(starting_at_one)) # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
```
---
### Reverse Ranges
```python=
reversed_maybe = range(51, 5)
print(list(reversed_maybe)) # []
reversed = range(51, 5, -1)
print(list(reversed)) # [51, 50, ... 9, 8, 7, 6]
```
---
### Iterating over Collections and Using Ranges
```python=
items = ['a', 'b', 'c']
for item in items:
print(item)
for i in range(len(items)):
print(i, items[i])
for i in range(1, 10, 2):
print(i)
```
---
## Practices 4 (7 min)
- Explore The Range (5:00)
- Range Loops (2:00)
---
## Lecture Videos 5 (10 minutes)
Watch:
- Dictionaries in Python (8:31)
Discuss Code Only:
- More Ways To Make A Dictionary (10:08)
---
### Dictionaries
- Dictionaries are ordered and mutable.
- They consist of pairs of keys and values.
- The keys must be "hashable" (immutable) values
- e.g. keys can be strings, numbers, tuples, booleans, but not lists or dictionaries
- Dictionaries use curly braces, similar to JavaScript objects, although keys do not have to be strings.
- but if the keys _are_ strings, they must use quotes, unlike JS
---
### Declaring Dictionaries
```python=
a = {'one':1, 'two':2, 'three':3}
b = dict(one=1, two=2, three=3)
c = dict([('two', 2), ('one', 1), ('three', 3)])
print(a == b == c) # True
```
If keys and values are the same, they are considered equivalent, doesn't matter how they were defined
---
### Using `[]` and `.get()` to index into a Dictionary
- Unlike javascript in Python you cannot index into a dictionary with dot notation
```python=
my_dict = {'one':1, 'two':2, 'three':3}
print(my_dict.one) #AttributeError: 'dict' object has no attribute 'one'
print(my_dict['one']) #1
print(my_dict['new']) # KeyError: 'new'
print(my_dict.get('one', None)) # 1
print(my_dict.get('gorilla', None)) # None
```
---
### Adding, Updating, Deleting Data in Dictionaries
- `del` keyword to delete a key/value pair
- `[]` to add or update a key/value pair
```python=
my_dict = {
"word": "cooool",
1: "one",
False: 42,
("tuple", "keys"): ["lists", "can", "be", "values", "not", "keys"],
None: {"key": "value"}
}
my_dict[1] = "two" # Updates value
print(my_dict[1]) # "two"
my_dict["new_key"] = "new_value" #Adds key/value
del my_dict["word"] # Deletes key/value
print(my_dict) # I promise, all the changes are there!
```
---
### Using `in`
- `in` allows you to check if a value is a key in the dictionary
```python=
print(1 in {1: "one", 2: "two"}) # True
print("1" in {1: "one", 2: "two"}) # False
print(4 in {1: "one", 2: "two"}) # False
print("one" in {1: "one", 2: "two"})# False
```
---
### .items()
- `.items()` returns a list of tuples
- Each tuple contains the key/value pair of the item in the dictionary
```python=
book = {
'title': 'Goodnight Moon',
'ratings': 7492,
'stars': 4.8,
'author': {'firstName': 'Margaret', 'lastName': 'Wise Brown'},
'images': ['goodnight1.png', 'goodnight2.png'],
}
for key, value in book.items():
print('key:', key, 'value:', value)
for key in book:
print('key:', key, 'value:', book[key])
```
---
### `.keys()` and `.values()`
- `.keys()` returns a list of the keys in the dictionary
- `.values()` returns a list of the values in the dictionary
```python=
book = {
'title': 'Goodnight Moon',
'ratings': 7492,
'stars': 4.8,
'author': {'firstName': 'Margaret', 'lastName': 'Wise Brown'},
'images': ['goodnight1.png', 'goodnight2.png'],
}
for value in book.values():
print('value:', value)
for key in book.keys():
print('key:', key)
```
---
## Practices 5 (15 minutes)
- Explore The Dictionary (5:00)
- Does The Dictionary Have A Key? (2:00)
- Is The Dictionary Empty? (1:00)
- Create Name Tag (5:00)
---
## Lecture Videos 6 (18 minutes)
Watch:
- Sets in Python (12:19)
- Using Sets (2:42)
Discuss code only:
- Combining Data Structures (7:32)
- Stack And Queue Overview (13:26)
---
### Sets
Sets are mutable, unordered collections where all elements are unique.
Sets use curly braces, like a dictionary. You can create an empty set with `set()`.
While sets can be mutated, the individual elements must be immutable types.
```python=
not_an_empty_set = {}
print(type(not_an_empty_set)) # <class 'dict'>
empty_set = set()
print(type(empty_set)) # <class 'set'>
set_with_elements = {1, "hello", None}
print(set_with_elements) # {1, None, "hello"}
no_duplicates = {1, 1, 1}
print(no_duplicates) # {1}
```
---
### Set operations
#### Union `(a | b)`

```python=
a = {1, 2, 3}
b = {3, 4, 5}
print(a | b) # {1, 2, 3, 4, 5}
print(a.union(b)) # {1, 2, 3, 4, 5}
# order of a and b does not matter
```
---
### Set operations
#### Intersection `(a & b)`

```python=
a = {1, 2, 3}
b = {3, 4, 5}
print(a & b) # {3}
print(a.intersection(b)) # {3}
# order of a and b does not matter
```
---
### Set operations
#### Symmetric difference `(a ^ b)`

```python=
a = {1, 2, 3}
b = {3, 4, 5}
print(a ^ b) # {1, 2, 4, 5}
print(a.symmetric_difference(b)) # {1, 2, 4, 5}
# order of a and b does not matter
```
---
### Set operations
#### Difference `(a - b)`

```python=
a = {1, 2, 3}
b = {3, 4, 5}
print(a - b) # {1, 2}
print(a.difference(b)) # {1, 2}
```
---
### Set operations
#### Difference `(b - a)`

```python=
a = {1, 2, 3}
b = {3, 4, 5}
print(b - a) # {4, 5}
print(b.difference(a)) # {4, 5}
```
---
### Sets operations, continued
With multiple operators, evaluation will happen from left to right.
```python=
a = {1, 2, 3}
b = {3, 4, 5}
c = {5, 6, 7}
print(a & b | c) # {3, 5, 6, 7}
print((a & b) | c) # {3, 5, 6, 7}
print(a & (b | c)) # {3}
```
---
### Creating sets from other iterables
You can use the `set()` function to create new sets from other iterable types.
```python=
my_list = [1, 2, 3, 4, 5, 5]
print(set(my_list)) # {1, 2, 3, 4, 5}
my_string = "hello"
print(set(my_string)) # {'l', 'h', 'o', 'e'}
my_tuple = (1, 1, 1)
print(set(my_tuple)) # {1}
my_dict = {"hello": "value", "goodbye": "value"}
print(set(my_dict)) # {'goodbye', 'hello'}
```
---
### Code from "Combining Data Structures"
```python=
# posts is a list of dictionaries, some of the values in that list
posts = [
{
"title": "All About Lists",
"tags": ("fun", "informative", "lists")
},
{
"title": "Tuple Trouble",
"tags": ("fun", "tuples")
},
{
"title": "Sparkling Sets",
"tags": ("informative", "numbers")
},
]
all_tags = [] # list to hold all tags
for i in range(len(posts)):
print(posts[i]["tags"])
all_tags.extend(posts[i]["tags"])
print(all_tags)
print(set(all_tags))
all_tags = list(set(all_tags))
all_tags.sort()
print(all_tags)
```
---
### Code example from "Stack and Queue Overview"
#### Interuption stack
```python=
teller = []
teller.append("Greet Customer")
print(teller)
teller.pop()
print(teller)
teller.append("Process Deposit")
print(teller)
teller.append("Phone Ringing")
print(teller)
teller.pop()
teller.append("Greet Caller, Listen, Answer Question")
print(teller)
teller.pop()
print(teller)
teller.pop()
print(teller)
```
---
### Code example from "Stack and Queue Overview"
#### Processing queue
```python=
processor = []
processor.append({'type':'page','path':'','header':[],'cookies':[]}),
processor.append({'type':'api', 'function':'', 'parameters':[]})
processor.append({'email':'email','address':'bob@gmail.com','subject':''})
print("PROCESSOR LIST", processor)
for i in range(len(processor)):
item = processor.pop(0)
print("PROCESSING ITEM", item)
print("REMAINING LIST", processor)
```
---
## Lecture Videos 7 (30 minutes)
Watch:
- Built-Ins: All And Any (8:00)
- Built-Ins: Filter and Map (10:32)
- Built-Ins: Custom Sort (9:05)
Discuss code only:
- Local Packages And Modules
---
### Built-in functions (`all()`)
The `all()` function checks that there are _no_ falsey items in the provided collection.
```python=
test1 = {"item", "truthy", ""}
test2 = []
test3 = [[]]
print(all(test1), test1)
print(all(test2), test2)
print(all(test3), test3)
```
---
### Built-in functions (`all()`)
Answers:
```python=
test1 = {"item", "truthy", ""}
test2 = []
test3 = [[]]
print(all(test1), test1) # False {"", "truthy", "item"}
print(all(test2), test2) # True []
print(all(test3), test3) # False [[]]
```
---
### Built-in functions (`any()`)
The `any()` checks that there is at least one truthy item in the provided collection
```python=
test1 = ["item", [], []]
test2 = []
test3 = [[]]
print(any(test1), test1)
print(any(test2), test2)
print(any(test3), test3)
```
---
### Built-in functions (`any()`)
Answers:
```python=
test1 = ["item", [], []]
test2 = []
test3 = [[]]
print(any(test1), test1) # True ['item', [], []]
print(any(test2), test2) # False []
print(any(test3), test3) # False [[]]
```
---
### Built-ins (`filter()`)
The `filter()` function takes a function and an iterable, and it returns a "filter object".
The returned collection includes only the items which, when the function parameter was applied to them, returned a truthy value.
```python=
def is_a(num):
if num >= 90:
return True
else:
return False
scores = [90, 86, 75, 91, 62, 99, 88, 90]
only_as = filter(is_a, scores) # does not mutate original
print(only_as) # <filter object at 0x10546ad30>
print(list(only_as)) # [90, 91, 99, 90]
```
---
### Built-ins (`filter()`)
`filter`'s function parameter can also be defined in line as a `lambda` function.
```python=
scores = [90, 86, 75, 91, 62, 99, 88, 90]
only_as = filter(lambda num: num >= 90, scores)
print(only_as) # <filter object at 0x10546ad30>
print(list(only_as)) # [90, 91, 99, 90]
```
---
### Built-ins (`map()`)
The `map()` function takes a function and an iterable, and it returns a "map object" that transforms each value from the original iterable, according to the provided function.
```python=
def get_grade(num):
if (num >= 90):
return "A"
elif (num <90 and num >= 80):
return "B"
elif (num < 80 and num >= 70):
return "C"
elif (num < 70 and num >= 60):
return "D"
else:
return "F"
scores = [90, 86, 75, 91, 62, 99, 88, 90]
print(map(get_grade, scores)) # <map object at 0x106faffa0>
grades = list(map(get_grade, scores))
print(grades) # ['A', 'B', 'C', 'A', 'D', 'A', 'B', 'A']
```
---
### Built-ins (`zip()`)
The `zip()` function takes two iterables and returns a "zip object" that pairs values at corresponding indices.
You can convert the "zip object" into a list of tuples with the `list()` function.
```python=
scores = [90, 86, 75, 91, 62, 99, 88, 90]
grades = ["A", "B", "C", "A", "D", "A", "B", "A"]
combined = zip(scores, grades)
combined_list = list(combined)
combined_dict = dict(combined_list)
print(combined) # <zip object at 0x1023a9600>
print(combined_list) # [(90, 'A'), (86, 'B'), (75, 'C'), (91, 'A'), (62, 'D'), (99, 'A'), (88, 'B'), (90, 'A')]
print(combined_dict) # {90: 'A', 86: 'B', 75: 'C', 91: 'A', 62: 'D', 99: 'A', 88: 'B'}
```
---
### Basic imports in Python
Basic imports in Python are backwards from JavaScript.
```python=
# from module_name import variable_name
from random import randint
print(randint(0, 10))
```
You can also import an entire module, and then access all the values from it using dot notation.
```python=
import random
print(random.randint(0, 10))
```
---
### ~~Exports in Python~~
Unlike JavaScript, Python does not require exports. All of the objects, classes, functions, etc. that are defined in a module are automatically available to import.
---
## Practices 7 (5 min)
- All Occurrences Of A Value In A List (5:00)
---
## Summary Practices (60 minutes)
- Guess A Number Game
- Bonus: Track The Robot
- Bonus: Averages
## Summary Quizzes (5 minutes)
- Structured Data Quiz (5:00)