# Lecture notes 2/3/2021
## Ranges
```python=
# # declaring ranges
# with just 1 argument, it will assume a start of 0
nums = range(5)
print(nums) # => Range(0,5)
print(list(nums)) # => [0,1,2,3,4]
# if the start value is a greater number than the stop, the
# range is empty
nums = range(-5)
print(list(nums)) # => []
# the range will include the "start" number and exclude the "stop"
nums_range = range(1, 11)
print(list(nums_range)) # => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# an optional third parameter will control step size
fives = range(0, 51, 5)
print(list(fives)) # => [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50]
# step size can be negative
test = range(50, 4, -5)
print(list(test)) # => [50, 45, 40, 35, 30, 25, 20, 15, 10, 5]
# loops with ranges
my_range = range(5)
for i in my_range:
print(i)
for i in range(1, 10, 2):
print(i)
items = ['apple', 'banana', 'cherry']
for i in range(len(items)):
print(i, items[i])
for i in items:
print(i)
```
# Dictionaries
```python=
# Dictionaries
# ordered and mutable
# pairs of keys and values
# keys must be "hashable" (immutable)
# e.g. strings, numbers, tuples, booleans
# declaring: use curly braces
# notice that unlike JS objects, keys do not have to be strings
# but keys that are strings must use quotes
my_dict = {
"word": "cooool",
1: "one",
False: 42,
("tuple", "keys"): ["lists", "can", "be", "values", "not", "keys"],
None: {"key": "value"}
}
print(my_dict)
# => {
# 'word': 'cooool',
# 1: 'one',
# False: 42,
# ('tuple', 'keys'): ['lists', 'can', 'be', 'values', 'not', 'keys'],
# None: {'key': 'value'}
# }
# length is the number of key:value pairs
print(len(my_dict)) # => 5
# accessing an individal value by key
print(my_dict[None]) # => {'key': 'value'}
# add/remove items
del my_dict[False]
print(my_dict)
my_dict["key"] = "value"
print(my_dict)
my_dict["key"] = "different value"
print(my_dict)
# # in JS can use dot or bracket notation for accessing values in objects
# console.log(my_dict.word)
# console.log(my_dict["word"])
# Python doesn't use dot notation for dictionaries!
print(my_dict["word"])
print(my_dict.word) # AttributeError: 'dict' object has no attribute 'word'
# in JavaScript, attempting to access a value that doesn't exist will
# just return undefined
# in python this will throw an error
print(my_dict["nope"]) # => KeyError: 'nope'
# you can use the get() method to safely access a key that may not exist
print(my_dict.get("nope", "yep")) # => "yep"
# conditionals with dictionaries
if "word" in my_dict:
print("word key exists")
# loops with dictionaries
for key in my_dict:
print(key, my_dict[key])
for key, value in my_dict.items():
print("key:", key, "value:", value)
# dict() can be used to define dictionaries using keyword args
# the keys can only be strings though
user = dict(
name="Marnie",
email="marnie@marnie.com",
friends=["Bonnie", "Sammie", "Lexie"],
)
print(user)
# dict() can also turn a list of tuples into a dict
list_of_tuples = [
("name","Bonnie"),
("email", "bonnie@bonnie.com"),
("friends", ["Marnie", "Sammie"]),
]
user2 = dict(list_of_tuples)
print(user2)
# # zip()
keys = ["peanut butter", "sugar", "egg"]
values = ["1 cup", "1 cup", 1]
recipe = dict(zip(keys, values))
print(recipe)
# digression:
# reassigning values vs mutability
# tuples are immutable, so you cannot modify them. however, you can still
# reassign the variable.
# this changes what location in memory the variable is pointing to.
# but the tuple itself is not changing
# you can illustrate this by checking the unique id of a variable
# before and after reassigning it
my_tuple = ("some stuff", "more")
print(id(my_tuple)) # => some long id number
my_tuple = 5
print(id(my_tuple)) # => a new long id number
# changing the contents of a list by appending to it will not change the id.
# it's still the same object!
my_list = ["hey"]
print(id(my_list)) # => some long id number
my_list.append("stuff")
print(id(my_list)) # => the same long id number
```
# Sets
## Set operations visualized
### Union `(a | b)`

### Intersection `(a & b)`

### Symmetric difference `(a ^ b)`

### Difference
#### `(a - b)`

#### `(b - a)`

```python=
# declaring sets
a = {1, 2, 3}
b = {3, 4, 5}
print(a) # => {1, 2, 3}
print(b) # => {3, 4, 5}
# methods: union, intersection, symmetric_difference, difference
print(a | b) # => {1, 2, 3, 4, 5}
print(a & b) # => {3}
print(a - b) # => {1, 2}
print(b - a) # => {4, 5}
print(a ^ b) # => {1, 2, 4, 5}
# print(a + b) # => error
# alternate syntax
print(a.union(b))
print(a.intersection(b))
print(a.symmetric_difference(b))
print(a.difference(b))
print(b.difference(a))
# with multiple operators, evaluation will happen from left to right!
c = {5, 6, 7}
print(a & b | c) # => {3, 5, 6, 7}
# you can make sets out of lists, strings, tuples
lst = [1,2,3,4,5,5]
print(set(lst)) # => {1, 2, 3, 4, 5}
string = "hello"
print(set(string)) # => {'l', 'h', 'o', 'e'}
print(set((1,1,1))) # => {1}
# and vice versa with lists and tuples
print(list(a)) # => [1, 2, 3]
# Some examples of times when sets are useful
# identifying overlapping values across different groups
# comments on posts
# traversing cyclical graphs
```
# Example Stacks and Queues using Lists
```python=
# Stacks and Queues
# stacks
# first in, last out
# when would use a stack?
# dispensing pez
# packing and unpacking
# iteratively doing depth first search
# interuption stack example
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)
# queues
# first in first out
# when would i use a queue?
# breadth first tree search
# processing of client orders
## processing queue
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)
```
# Built-in Functions
```python=
# Built-in functions
# all() checks that there are no falsey items
test1 = {"item", "truthy", ""}
test2 = []
test3 = [[]]
print(all(test1), test1) # => False {'', 'truthy', 'item'}
print(all(test2), test2) # => True []
print(all(test3), test3) # => False [[]]
# any() checks that there is at least one truthy item
test1 = ["item", [], []]
test2 = []
test3 = [[]]
print(any(test1), test1) # => True ['item', [], []]
print(any(test2), test2) # => False []
print(any(test3), test3) # => False [[]]
# filter
scores = [90, 86, 75, 91, 62, 99, 88, 90]
print(scores)
def isA(num):
if num >= 90:
return num
else:
return False
aScores = filter(isA, scores)
# aScores = filter(lambda num: num >= 90, scores) # this would also work
print(aScores)
print(list(aScores))
print(scores)
def getGrade(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"
grades = list(map(getGrade, scores))
print(grades)
# print("ZIPPED GRADES AND SCORES")
combined = zip(scores, grades)
combined_list = list(zip(scores, grades))
print(combined)
print(combined_list)
# getting unique usernames
comments = [
("Marni", "hi this is fun"),
("bob", "no its not"),
("Marni", "okay, u r right bob thx"),
]
def get_username(comment):
return comment[0]
usernames = list(map(get_username, comments))
print(usernames)
unique_commenters = set(usernames)
# custom sorting
def sorter(comment):
return comment[0].lower()
print(comments)
comments.sort(key=sorter, reverse=True)
print(comments)
```