JS Symbol Iterators & Generators - An Exercise 
Tags: javascript, iterables, generators, symbols
Published: 2021-02-01 11:03:43

---
title: JS Symbol Iterators & Generators - An Exercise 
published: true
description: Let's implement a function together that generates a list for us, and yields a value specific to what gets passed in.
tags: javascript, iterables, generators, symbols
//cover_image: https://direct_url_to_image.jpg
---

***
<h4>
<center>
 What is function*, yield and yield*? 
</center>
</h4>
***

  * [Brief](#brief)
  * [A Linked List](#a-linked-list)
* [Iterator Logic](#iterator-logic)
    * [yield*](#yield)
    * [Reverse the data](#reverse-the-data)
    * [Did the child become a parent?](#did-the-child-become-a-parent)
  * [Let's Apply it and Test](#lets-apply-it-and-test)
  * [Repl](#repl)
* [Recap](#recap)
    * [Destructuring parameters from next via yield](#destructuring-parameters-from-next-via-yield)

## Brief

When you iterate over lists it's probably intuitive now for most to use `Array.map()`. However, many of us also like to generate our lists based on some range, not by way of data but some application logic defined number. Usually, I'll import `lodash.range` or create a range helper. What about specific sequences like Fibonacci? Well here we can take the power of large, possibily infinite size lists. Normally large lists would hurt performance if it's particularly large even in `O(n)`. 

Essentially we're creating a lazy loaded sequence.   

In a lot of libraries today we have lazy load as a common way of handling lists of data that may be large in length or in size per element; image galleries.  

Without writing a lot of helpers, importing libraries, or getting caught in cumbersome type associations as boilerplate, we can look at built-in Generators. 

Now when we define our applications sequences or even the json we may use, we can immediately "close the faucet" of the flow of that data. Only opening it when we need it, making it reusable, and allowing us to completely throw it out if we need to start over. 

---

## A Linked List

Given a list of data we can look at a list of lists to get started:

```jsx
const familyTree = [
    ["Adam", "Jane", "Doe"],
    ["Jane", "Peter", "Mary"],
    ["Mary", "Liam", "Olivia"],
    ["William", "Ava", "Lucas"]
]
```

Here we have a "sorted" list going from `familyTree[0]` being the earliest generation and last index being the oldest. 

Let's assume that the first of each is the "Child" and the other two are biological "Parents".

# Iterator Logic

Let's start by creating our `familyTree` iterator logic. 

```jsx
function* genList(p1, p2) {
	const genealogy = [...familyTree].reverse();
}
```

I choose to work backwards from generation, given our data, and spread operator to prevent mutation. 

In this data our familyTree contains the newest generation at the head or first of the list. So we can reverse the list before we start.

### yield* 

We could easily create a map of each element very quickly with `yield*` to simply "iterate" the given data, and give us each Array within `familyTree`, but where's the fun in that. Our generator should have some logic, and `yield genealogy[i]` conditionally!
  
To clarify what `*` does, we can look at `yield*`:

```js
function* genList() {
   yield* [...familyTree].reverse();
}

let i = genList();
console.log(i.next().value); //  [ 'William', 'Ava', 'Lucas' ] 
console.log(i.next().value); //  [ 'Mary', 'Liam', 'Olivia' ] 
```


Now let's search to find who we're actually looking for with `p2` or person2

- Let's imagine it's "Olivia"
    - ["William", "Ava", "Lucas"] is first since we reversed, so we can skip over it

### Reverse the data

```jsx
function* genList(p1, p2) {
	const genealogy = [...familyTree].reverse();
	let start = 0, end = genealogy.length - 1;
	for (const i in genealogy) {
		if (genealogy[i].includes(p2)) {
			start = +i // coerce typeof i to number from string
		}
		if (genealogy[i].includes(p1)) {
			// Exercise: what would go here, and why?
			// leave a comment below 😉
		}
	}
```

Now that we can rule out names that aren't even in here. 

Let's loop through our reduced list by finding links in each family array, for preceding arrays;

- or "blood" relation
- see: Linked List

### Did the child become a parent?

*main.js*

```jsx
function* genList(p1, p2) {
	[...]


	// |-> Read along
	// Iterator Logic
	for (let i = start; i <= end; i++) {
		// yield will send over the first family
		let link = yield genealogy[i]
		// with .next(Child) we can pass over a name 
		// from the above: yield genealogy[i]
		// to the above: link
		if (link && (i + 1) <= end) {
			let [_, ...parents] = genealogy[i + 1]
			// Did that child became a parent?                      
			// Let's see if parents include the link
			if (parents.includes(link)) {
				yield genealogy[i]
			}
		} else {
			// but if there's no subsequent links...
			break;
		}
	}
}

```

## Let's Apply it and Test

*main.js*

```jsx
/**
 * 
 * @param p1 Child
 * @param p2 Relative
 */
const isGenerational = (p1, p2) => {
	let link;

	// generate genealogy with lower and upper bounds
	const ancestry = genList(p1, p2)
	// get Child from each family and yield links 
	for (const [ancestor] of ancestry) {
		(ancestor === p1)
			// if p1 is found, we can throw the list away
			? link = ancestry.return(true)
			// if linked list continues
			: link = ancestry.next(ancestor)

	}
	return (link.done && link.value)
}

(async () => {
    console.log(
        (isGenerational("Adam", "Olivia") === true),
        (isGenerational("Adam", "Lucas") === false),
        (isGenerational("Jane", "Liam") === true),
        (isGenerational("Mary", "Ava") === false),
    )
})();
```
## Repl

{% replit @teachtyler/Iterators-and-Generators %}

# Recap

### Destructuring parameters from next via yield

Let's look at this very peculiar statement.  

```jsx
let link = yield genealogy[i]
```

It's the initialization that makes it useful. 

We can send data over initially, and wait for any data that may be contextual. 

```jsx
let whatAboutThis = yield context[i]
if (whatAboutThis) {
	// perform check, updates, hydrate, whatever
	await updateContext(whatAboutThis)
	yield context["imaginary"]
}
```

Essentially when our function obtains something we can have our iterator pass it up to the generator and allocate a new yielded value. 

```jsx
const iterator = contextSequence(); // generates sequence of "context"
let water = iterator.next("hydrate"); // <- gets passed to `whatAboutThis` 
water.value // -> value stored of context["imaginary"] 
```

I can imagine reactive state handlers here. Imagine a federal reserve of some data that is only accessed when stores are low; lazy loaded galleries<images>. 

Great way to handle large queues that run async but not AOT. 

I'm thinking debounce function for subscribed events that aren't time critical. Although I'll need to play around with it a bit. Almost every example showcases `take` on infinite lists, so it's very functional.