# Lists

In Python, lists are ordered collections of items that allow for easy use of a set of data.

List values are placed in between square brackets `[ ]`, separated by commas. It is good practice to put a space between the comma and the next value. The values in a list do not need to be unique (the same value can be repeated).

Empty lists do not contain any values within the square brackets.

```python
primes = [2, 3, 5, 7, 11]
print(primes)

empty_list = []
```

## Data Types in Lists

In Python, lists are a versatile data type that can contain multiple different data types within the same square brackets. The possible data types within a list include numbers, strings, other objects, and even other lists.

```python
numbers = [1, 2, 3, 4, 10]
names = ['Jenny', 'Sam', 'Alexis']
mixed = ['Jenny', 1, 2]
list_of_lists = [['a', 1], ['b', 2]]
```

## **Converting from another type**

```python
list('abc')      # ['a','b','c']
list((1,2,3))    # [1,2,3]
list({1,2,3})    # [1,2,3]
```

## Growing a List: Append

In Python, you can add values to the end of a list using the `.append()` method. This will place the object passed in as a new element at the very end of the list.&#x20;

```python
orders = ['daisies', 'periwinkle']
orders.append('tulips')
print(orders)
# Result: ['daisies', 'periwinkle', 'tulips']
```

## Growing a List: Plus (+)

In Python, lists can be added to each other using the plus symbol `+`.  This will result in a new list containing the same items in the same order with the first list’s items coming first.

```python
items = ['cake', 'cookie', 'bread']
total_items = items + ['biscuit', 'tart']
print(total_items)
# Result: ['cake', 'cookie', 'bread', 'biscuit', 'tart']
```

An alternative way is to use `.extend()`**.**

```python
mylist = ['a', 'b', 'c']
mylist.extend([1,2,3])
mylist                  # ['a', 'b', 'c', 'd', 1, 2, 3]

```

**Note:** This will not work for adding one item at a time (use `.append()` method). In order to add one item, create a new list with a single value and then use the plus symbol to add the list.

```python
mylist = ['a', 'b', 'c']
mylist += ['d']
mylist                  # ['a', 'b', 'c', 'd']
```

## Range

`range()` is used to create a list of consecutive numbers.

By default, `range()` takes a single input, and generates numbers starting at `0` and ending at the number **before** the input.&#x20;

```python
>>> my_range = range(10)
>>> print(list(my_range))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
```

If we call `range()` with two arguments, we can create a list that starts at a different number. For example, `range(2, 9)` would generate numbers starting at `2` and ending at `8` (just before `9`):

```python
>>> my_range2 = range(2, 9)
>>> print(list(my_range2))
[2, 3, 4, 5, 6, 7, 8]
```

If we use a third argument, we can create a list that “skips” numbers. For example, `range(2, 9, 2)` will give us a list where each number is `2` greater than the previous number:

```python
>>> my_range3 = range(2, 9, 2)
>>> print(list(my_range3))
[2, 4, 6, 8]
```

## Determining List Length with`len()`

The Python `len()` function can be used to determine the number of items found in the list it accepts as an argument.

```python
knapsack = [2, 4, 3, 7, 10]
size = len(knapsack)
print(size) 
# Output: 5
```

## Selecting List Elements

In Python, we call the order of an element in a list its *index*. Python lists are *zero-indexed*. This means that the first element in a list has index `0`, rather than `1`.

#### Selecting one element from the beginning

We can select a single element from a list by using square brackets (`[]`) and the index of the list item.&#x20;

```python
berries = ["blueberry", "cranberry", "raspberry"]

berries[0]   # "blueberry"
berries[2]   # "raspberry"
```

#### Selecting one element from the end

Negative indices for lists in Python can be used to reference elements in relation to the end of a list.

```python
soups = ['minestrone', 'lentil', 'pho', 'laksa']
soups[-1]   # 'laksa'
```

## Slicing Lists

A *slice*, or sub-list of Python list elements can be selected from a list using a colon-separated starting and ending point.

The syntax pattern is `myList[START_NUMBER:END_NUMBER]`. The slice will include the `START_NUMBER` index, and everything until but excluding the `END_NUMBER` item.

When slicing a list, a new list is returned, so if the slice is saved and then altered, the original list remains the same.

```python
tools = ['pen', 'hammer', 'lever']
tools_slice = tools[1:3] # ['hammer', 'lever']
tools_slice[0] = 'nail'

# Original list is unaltered:
print(tools) # ['pen', 'hammer', 'lever']
```

When selecting a range of list items, if the first item to be selected is at index `0`, no index needs to be specified before the `:`. Similarly, if the last item selected is the last item in the list, no index needs to be specified after the `:`

```python
items = [1, 2, 3, 4, 5, 6]

# All items from index `0` to `3`
print(items[:4])

# All items from index `2` to the last item, inclusive
print(items[2:])
```

#### Slicing skipped elements

```python
A = [0, 1, 2, 3, 4, 5]
A[1:5:2]    # [1, 3]
A[5:1:-2]   # [5, 3]
```

## Negative List Indices

Negative indices for lists in Python can be used to reference elements in relation to the end of a list. This can be used to access single list elements or as part of defining a list range. For instance:

* To select the last element, `my_list[-1]`.
* To select the last three elements, `my_list[-3:]`.
* To select everything except the last two elements, `my_list[:-2]`.

```python
soups = ['minestrone', 'lentil', 'pho', 'laksa']
soups[-1]   # 'laksa'
soups[-3:]  # 'lentil', 'pho', 'laksa'
soups[:-2]  # 'minestrone', 'lentil'
```

## Counting elements in a list

The `.count()` Python list method searches a list for whatever search term it receives as an argument, then returns the number of matching entries found.

```python
backpack = ['pencil', 'pen', 'notebook', 'textbook', 
                'pen', 'highlighter', 'pen']
numPen = backpack.count('pen')
print(numPen)
# Output: 3
```

## Sorting

The `.sort()` Python list method will sort the contents of whatever list it is called on. Numerical lists will be sorted in ascending order, and lists of Strings will be sorted into alphabetical order. It modifies the original list, and has no return value.

```python
exampleList = [4, 2, 1, 3]
exampleList.sort()
print(exampleList)
# Output: [1, 2, 3, 4]
```

The Python `sorted()` function accepts a list as an argument, and will return a new, sorted list containing the same elements as the original. Numerical lists will be sorted in ascending order, and lists of Strings will be sorted into alphabetical order. It does not modify the original, unsorted list.

```python
unsortedList = [4, 2, 1, 3]
sortedList = sorted(unsortedList)
print(sortedList)
# Output: [1, 2, 3, 4]
```

## Aggregating Iterables Using`zip()`

In Python, data types that can be iterated (called iterables) can be used with the `zip()` function to aggregate data based on the iterables passed in.

`zip` takes two (or more) lists as inputs and returns an *object* that contains a list of pairs. Each *pair* contains one element from each of the inputs.

As shown in the example, `zip()` is aggregating the data between the owners’ names and the dogs’ names to match the owner to their dogs. `zip()` returns an iterator containing the data based on what the user passes through and can be printed to visually represent the aggregated data. Empty iterables passed in will result in an empty iterator.

```python
owners_names = ['Jenny', 'Sam', 'Alexis']
dogs_names = ['Elphonse', 'Dr. Doggy DDS', 'Carter']
owners_dogs = zip(owners_names, dogs_names)
print(owners_dogs)
# Result: [('Jenny', 'Elphonse'), 
# ('Sam', 'Dr.Doggy DDS'), ('Alexis', 'Carter')]
```

## **Checking membership**

```python
3 in mylist    # True or False
               #  O(n) time complexity
mylist.index(3)  # get the index of a element
```

## **Shallow copy**

```python
# shallow copy a list
B = A[:]
```

## **Replacing an existing element**

```python
mylist = ['a', 'b', 'c']
mylist[1] = 'z'
mylist           # ['a', 'z', 'c']
```

#### **Replacing multiple existing elements**

```python
mylist = ['a', 'b', 'c', 'd', 'e', 'f']
mylist[1:3] = 'xyz'     # replace indexes 1 and 2 with x, y, z
mylist # ['a', 'x', 'y', 'z', 'd', 'e', 'f']
```

## **Insert a element into a list at specific index**

```python
# 1st way: use insert() function
mylist = ['a', 'b', 'c']
mylist.insert(1, 'e')
mylist               # ['a', 'e', 'b', 'c']

# 2nd way: use slicing
mylist = ['a', 'b', 'c']
mylist[1:1] = 'e'
mylist               # ['a', 'e', 'b', 'c']
```

## **Removing elements**

#### **Removing an element from the end**

```python
mylist = ['a', 'b', 'c']
mylist.pop()          # returns 'c'
mylist                # ['a', 'b']
```

#### **Removing an element from any index**

```python
# 1st way
mylist = ['a', 'b', 'c']
mylist.pop(0)        # returns 'a'
mylist               # ['b', 'c']

# 2nd way
mylist = ['a', 'b', 'c']
del mylist[0]        # ['b', 'c']
```

#### **Removing an element based on its value (rather than its position)**

```python
mylist = ['a', 'b', 'c', 'a', 'a', 'b']
mylist.remove('a')     # Remove the first 'a'
mylist                 # ['b', 'c', 'a', 'a', 'b']
```

#### **Removing a slice**

```python
mylist = ['a', 'b', 'c']
del mylist[0:1]      # ['c']
```

## **Reversing**

```python
# 1st way: in-place
mylist = ['a', 'b', 'c']
mylist.reverse()      # returns None
mylist                # ['c', 'b', 'a']

# 2nd way
mylist = ['a', 'b', 'c']
mylist = list(reversed(mylist))
mylist                # ['c', 'b', 'a']

# 3rd way
mylist = ['a', 'b', 'c']
mylist = mylist[::-1]
mylist                # ['c', 'b', 'a']
```

## **Iterating over the elements**

```python
# 1st way
mylist = ['a', 'b', 'c']
for item in mylist:
    print(item)

# 2nd way
mylist = ['a', 'b', 'c']
for i in range(len(mylist)):
    print(mylist[i])
```

#### **Iterating over the sorted elements**

```python
mylist = ['d', 'a', 'c', 'b']
for item in sorted(mylist):
    print(item)
```

## **Find max/min value in a list**

```python
mylist = [1, 2.33]
max(mylist)   #2.33
min(mylist)   #1
```
