Using zip

The [zip docs](https://docs.python.org/3/library/functions.html#zip) say that `zip(*iterables, strict=False)`: > Iterate over several iterables in parallel, producing tuples with an item from each one.
## Showing the Lists
I use fastcore's `L` to show the lists.
```python from fastcore.all import L ```
Otherwise the cell output is a `zip` object:
```python zip(range(2)) ```
## Create 2 Iterables
```python l1 = L(range(5)) l1 ```
```python l2 = L(range(6,11)) l2 ```
## Zip the 2 Iterables Together
The most common scenario is zipping 2 same-length iterables together:
```python L(zip(l1,l2)) ```
## Zip a Long and Short Iterable Together
It stops when the shortest iterable is exhausted:
```python L(zip(l1,l2[3:])) ```
## Zip 1 Iterable
With 1 iterable, you get an iterator of tuples:
```python L(zip(l1)) ```
## Zip Nothing
With no args, you get an empty iterator:
```python L(zip()) ```
## Zip Dict Items
`items` gives you an iterator of tuples, each tuple being a k,v pair:
```python d = {1: "one", 2: "two", 3: "three"} L(d.items()) ```
Destructuring and zipping that transposes the tuples:
```python L(zip(*d.items())) ```
## Application: Zip a Dict Tree
This tree is from [Jeremy Howard's bfs tweet](https://x.com/jeremyphoward/status/1873810934608912550):
```python tree = {1: {2: {4: {}}, 3: {5: {}, 6: {}}}} L(tree) ```
It represents this:
``` 1 ├── 2 │ └── 4 └── 3 ├── 5 └── 6 ```
Breadth-first search (BFS) would traverse the tree from shallow to deep, printing each number as: `1 2 3 4 5 6`
Jeremy implemented BFS as:
```python def bfs(q): while q: k,v = zip(*q) print(*k) q = [x for o in v for x in o.items()] ```
To use it, he calls it with the tree's items:
```python bfs(tree.items()) ```
The tree's items are
```python L(tree.items()) ```
```python L(zip(*tree.items())) ```
Current node:
```python L(zip(*tree.items()))[0] ```
Child nodes:
```python L(zip(*tree.items()))[1] ```
### Why zip in bfs()?
The `zip` in `k,v = zip(*q)` serves to separate node values from their children at each level. Let's see how: 1. First iteration: ```python q = [(1, {2: {4: {}}, 3: {5: {}, 6: {}}})] k,v = zip(*q) # k=(1,), v=({2: {4: {}}, 3: {5: {}, 6: {}}},) ``` 2. Second iteration: ```python q = [(2, {4: {}}), (3, {5: {}, 6: {}})] k,v = zip(*q) # k=(2,3), v=({4: {}}, {5: {}, 6: {}}) ``` 3. Third iteration: ```python q = [(4, {}), (5, {}), (6, {})] k,v = zip(*q) # k=(4,5,6), v=({},{},{}) ``` The `zip` cleanly separates values (`k`) to print from their children (`v`) needed for the next level of traversal.