---
# System prepended metadata

title: Lecture notes 2/3/2021

---

# 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)`
![](https://i.imgur.com/5bY2S1u.png)
### Intersection `(a & b)`
![](https://i.imgur.com/dyvvZAv.png)
### Symmetric difference `(a ^ b)`
![](https://i.imgur.com/k2leBRy.png)
### Difference
#### `(a - b)`
![](https://i.imgur.com/soh0G8N.png)
#### `(b - a)`
![](https://i.imgur.com/UhNIrjj.png)

```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)
```
