# Introduction

[![Test Coverage](https://api.codeclimate.com/v1/badges/bade509a61c126d7f488/test_coverage)](https://codeclimate.com/github/tdreyno/leisure/test_coverage) [![npm latest version](https://img.shields.io/npm/v/@tdreyno/leisure/latest.svg)](https://www.npmjs.com/package/@tdreyno/leisure)

`leisure` is a TypeScript library for creating and manipulating lazy sequences of data. The API surface resembles the ES6 `map`/`filter`/`reduce` API, but also adds many useful helpers often seen in [Lodash](https://lodash.com/) or [Ramda](https://ramdajs.com/).

The difference between `leisure` and those non-lazy libraries is the guarantee that no computations will happen unless the result is actually used. Here's an example:

```typescript
type User = { name: string; address: string };
type Address = { city: string; streetNumber: number; owner: User };

const parseAddress = (address: string): Address => { /* Does parsing */ }

// Assume we have a list of 100 users from the database.
const users: User[] = [ user1, user2, user3, ..., user99, user100 ];

// Parse the data and find the users who live in my hometown.
const neighbor = users
  .map(user => parseAddress(user.address))
  .filter(address => address.city === "My Hometown")
  .map(address => address.owner)[0];
```

The above example will run `map` across the entire list of users. The code will parse 100 addresses. In this trivial example, that computation is likely very fast, but imagine if it were more intensive (say, using computer vision in the browser to detect a landmark). The `filter` will then also run 100 times to find the subset of addresses we are looking for. Then the second `map` will run 100 more times (admittedly convoluted). The total time of the computation is `O(3N)`.

At the end of the computation we discover that we only need the first matching result. If we were to operate on this data in a lazy fashion, the best case scenario is that the user we are looking for is first in this list. Utilizing lazy execution the first `map` would one once on the first user, that resulting address would run once through the `filter` and pass, then that address would run once more through the second `map`. The total computation would be `O(3)`. The worst case computation, where there is only 1 match and it is at the very end would be as slow as the original, but every single other permutation would be faster using the lazy approach.

Here is that same example using `leisure`:

```typescript
import { fromArray } from "@tdreyno/leisure"

// Assume the types and data are the same.
const neighbor = fromArray(users)
  .map(user => parseAddress(user.address))
  .filter(address => address.city === "My Hometown")
  .map(address => address.owner)
  .first()
```

As you can see, the API is nearly identical, with the exception of the `first` helper method.

## Infinite sequences

One of the very interesting side-effects of being lazy is the ability to interact with infinite sequences and perform the smallest number of possible computations to search over all the possibilities. For a very contrived example, find me the first number in the Fibonacci sequence that contains my birthday (let's say I was born at the beginning of the millennium: `2000_01_01`).

```typescript
import Seq from "@tdreyno/leisure"

const myBirthDay: number = 2000_01_01

const myBirthDayFib: number = Seq
  // Generate an infinite sequence that adds one Fib value each step.
  .iterate(([a, b]) => [b, a + b], [0, 1])

  // Find the one which has my birthday as a substring.
  .find(([fib]) => fib.toString().includes(myBirthDay.toString()))

  // Get just the number, not the pair.
  .map(([fib]) => fib)
```

Maybe it will run forever? Maybe it will find one very fast :)

## Realization

Most methods in the library ask for one value from the previous computation at a time. However, some require the entire sequence of values. The process of converting a lazy sequence into a non-lazy version is called "realization." The `.first` and `.find` methods we've been using are examples. They take a Sequence and return a normal JavaScript value.

Here's an example which discards the first 10 numbers of an infinite sequence, then grabs the next five and uses them.

```typescript
import { infinite } from "@tdreyno/leisure"

const tenThroughFourteen: number[] = infinite()
  // Ignore 0-9
  .skip(10)
  // Grab 10-14
  .take(5)
  // Realize the sequence into a real array
  .toArray()
```

There are a handful of methods which require the entire sequence, which means they will run infinitely if given an infinite sequence. They are: `toArray`, `forEach`, `sum`, `sumBy`, `average`, `averageBy`, `frequencies` and `groupBy`.

If logging and debugging in a chain of lazy processing is not running when you expect it to, remember to realize the sequence with a `.toArray()` to flush the results.

To avoid infinite loops, `leisure` caps the maximum number of infinite values to `1_000_000`. This number is configurable by setting `Seq.MAX_YIELDS` to whatever you want your max loop size to be.

## Integration with non-lazy JavaScript

`leisure` implements the Iterator protocol which means you can use it to lazily pull values using a normal `for` loop.

```typescript
import { infinite } from "@tdreyno/leisure"

for (let i of infinite()) {
  if (i > 10) {
    console.log("counted to ten")
    break
  }
}
```

## Common usage

`leisure` can be used as a drop-in replacement for the ES6 `map`/`filter`/`reduce` API. If that's all you need, you can still benefit from the reduction in operations in the non-worst-case scenarios.

However, if you dig into `leisure`'s helper methods, you'll be able to write more expressive data computations. With the ES6 API, developers often drop into `reduce` for more complex data transformations. These reducer bodies can be difficult to read and understand how they are actually transforming the data. `leisure` provides methods which do these common transformations and names them so your code is more readable. Dropping all the way down into a reducer is quite rare in `leisure` code.

[Take a look at the full `leisure` API.](/api/instance)

## Benchmarks

Lazy data structures should be used knowing what type of data you have and how to make the most of laziness. In general, you want to be searching large sets and either expect to find your result in the process.

**Please take these benchmarks with a grain of salt. Nobody is actually searching massive spaces on the web. Even the worst performance is still far faster than you'd need or notice unless you're doing graphics or crypto.**

These benchmarks search from a random array of 10,000 items.

Run `yarn perf` from the repository to reproduce these numbers.

### Best case (first item is all we need).

| approach | ops per second    |
| -------- | ----------------- |
| leisure  | 6,574,041 ops/sec |
| native   | 18,671 ops/sec    |
| lodash   | 11,602 ops/sec    |

When we find what we are looking for early, our performance is outstanding.

### middle case (we need to search half the space).

| approach | ops per second |
| -------- | -------------- |
| leisure  | 7,011 ops/sec  |
| native   | 18,897 ops/sec |
| lodash   | 9,850 ops/sec  |

When the result is in the middle, we are comperable in speed to lodash.

### Worst Case (we search the entire space)

| approach | ops per second |
| -------- | -------------- |
| leisure  | 3,304 ops/sec  |
| native   | 20,487 ops/sec |
| lodash   | 10,357 ops/sec |

In the absolute worst case, we are 3x as slow as lodash.

## Prior Art

This library takes inspiration from Lazy Sequences in [Clojure](https://clojure.org/reference/sequences), [Kotlin](https://kotlinlang.org/docs/reference/sequences.html) and [F#](https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/sequences).

[Lazy.js](https://github.com/dtao/lazy.js) is a popular lazy sequence library for JavaScript, but it was not written in TypeScript and I prefer to use libraries that think about types and transformations from the beginning.

There is some overlap between Lazy Sequences and the Functional Reactive Programming libraries. In my opinion, FRP is more concerned about event systems and Lazy Sequences are more useful for data processing.

## License

Leisure is licensed under the the [Hippocratic License](https://firstdonoharm.dev). It is an [Ethical Source license](https://ethicalsource.dev) derived from the MIT License, amended to limit the impact of the unethical use of open source software.


# Installation

`leisure` can be installed using Yarn (or npm if you really need to).

```bash
$ yarn add @tdreyno/leisure
```

## Importing

Usually, you will be importing a Sequence creating method. [See the full list here](/api/static).

```typescript
import { fromArray } from "@tdreyno/leisure"
```


# F.A.Q.

## Why is this page so empty?

This is a brand new project, we don't know what questions are being frequently asked.


# Static Methods

## fromArray

Takes a normal JavaScript array and turns it into a Sequence of the same type.

{% tabs %}
{% tab title="Usage" %}

```typescript
const sequence: Seq<number> = fromArray([1, 2, 3])
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type fromArray<T> = (data: T[]) => Seq<T>
```

{% endtab %}
{% endtabs %}

## iterate

The `iterate` method simplifies the construction of infinite sequences which are built using their previous value.

{% tabs %}
{% tab title="Usage" %}

```typescript
// Builds an infinite sequence that counts up from 0.
const sequence: Seq<number> = iterate(a => a + 1, 0)
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type iterate = <T>(fn: (current: T) => T, start: T): Seq<T>
```

{% endtab %}
{% endtabs %}

## fib

The `fib` method generates a sequence of fibonacci numbers.

{% tabs %}
{% tab title="Usage" %}

```typescript
const sequence: Seq<number> = fib()
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type fib = () => Seq<number>
```

{% endtab %}
{% endtabs %}

## random

Generates a random sequence using `Math.random`.

{% tabs %}
{% tab title="Usage" %}

```typescript
// Builds sequence of random numbers between 0 and 1.
const sequence: Seq<number> = random()
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type random = () => Seq<number>
```

{% endtab %}
{% endtabs %}

## range

Creates a sequence that counts between a start and end value. Takes an optional step parameter.

{% tabs %}
{% tab title="Usage" %}

```typescript
// Step by 1 from 0 through 10.
const sequence: Seq<number> = range(0, 10)
```

```typescript
// Step by 1 from 0 through Infinity.
const sequence: Seq<number> = range(0, Infinity)
```

```typescript
// Step by 1 from -10 through +10.
const sequence: Seq<number> = range(-10, 10)
```

```typescript
// Step down from +10 through -10.
const sequence: Seq<number> = range(10, -10)
```

```typescript
// Step by 2 from 0 through 10.
const sequence: Seq<number> = range(0, 10, 2)
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type range(
  start: number,
  end: number,
  step = 1
) => Seq<number>;
```

{% endtab %}
{% endtabs %}

## infinite

A sequence that counts from `0` to `Infinity`.

{% tabs %}
{% tab title="Usage" %}

```typescript
const sequence: Seq<number> = infinite()
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type infinite = () => Seq<number>
```

{% endtab %}
{% endtabs %}

## of

A sequence with only a single value inside. Also known as "singleton."

[Similar to Array.of](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/of).

{% tabs %}
{% tab title="Usage" %}

```typescript
const sequence: Seq<number> = of(2)
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type of = <T>(...values: T[]): Seq<T>;
```

{% endtab %}
{% endtabs %}

## cycle

Create a sequence that is the infinite repetition of a series of values.

{% tabs %}
{% tab title="Usage" %}

```typescript
const sequence: Seq<string> = cycle(["a", "b", "c"])
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type cycle = <T>(items: T[]) => Seq<T>
```

{% endtab %}
{% endtabs %}

## repeat

Create a sequence which repeats the same value X number of times. The length of the sequence defaults to Infinity.

{% tabs %}
{% tab title="Usage" %}

```typescript
// Creates a sequence of 5 true values.
const sequence: Seq<boolean> = repeat(true, 5)
```

```typescript
// Creates a sequence of 0 which repeats infinitely.
const sequence: Seq<number> = repeat(0)
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type repeat = <T>(value: T, times = Infinity) => Seq<T>
```

{% endtab %}
{% endtabs %}

## repeatedly

Creates a sequence which pulls from an impure callback to generate the sequence. The second parameter can cap the length of the sequence. By default, it will call the callback infinitely to generate values. Useful for asking about current time or cache values.

{% tabs %}
{% tab title="Usage" %}

```typescript
const sequence: Seq<Date> = repeatedly(() => new Date())
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type repeatedly = <T>(value: () => T, times = Infinity) => Seq<T>
```

{% endtab %}
{% endtabs %}

## empty

A sequence with nothing in it. Useful as a "no op" for certain code-paths when joining sequences together.

{% tabs %}
{% tab title="Usage" %}

```typescript
const sequence: Seq<never> = empty()
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type empty = () => Seq<never>
```

{% endtab %}
{% endtabs %}

## zip

Takes two sequences and lazily combines them to produce a tuple with the current step in each of the two positions. Useful for zipping a sequence of keys with a sequence of values, before converting to a Map of key to value.

{% tabs %}
{% tab title="Usage" %}

```typescript
const seq1 = fromArray(["zero", "one", "two", "three"])
const seq2 = range(0, 3)

// Gives: ["zero", 0] -> ["one", 1] -> ["two", 2] -> ["three", 3]
const sequence: Seq<[string, number]> = zip(seq1, seq2)
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type zip = <T1, T2>(
  seq1: Seq<T1>,
  seq2: Seq<T2>,
) => Seq<[T1 | undefined, T2 | undefined]>
```

{% endtab %}
{% endtabs %}

## zipWith

Takes two sequences and lazily combines them to produce an arbitrary value by mapping the current value of the two positions through a user-supplied function. Useful for table (row/col) math.

{% tabs %}
{% tab title="Usage" %}

```typescript
const seq1 = range(0, 3)
const seq2 = repeat(2)

// Gives: 0 -> 2 -> 4 -> 6
const sequence: Seq<number> = zipWith(
  ([num, multiplier]) => num * multiplier,
  seq1,
  seq2,
)
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type zipWith = <T1, T2, T3>(
  fn: (
    [result1, result2]: [T1, T2] | [T1, undefined] | [undefined, T2],
    index: number,
  ) => T3,
  seq1: Seq<T1>,
  seq2: Seq<T2>,
) => Seq<T3>
```

{% endtab %}
{% endtabs %}

## zip3

Takes three sequences and lazily combines them to produce a 3-tuple with the current step in each of the three positions.

{% tabs %}
{% tab title="Usage" %}

```typescript
const seq1 = fromArray(["zero", "one", "two", "three"])
const seq2 = range(0, 3)
const seq3 = range(3, 0)

// Gives: ["zero", 0, 3] -> ["one", 1, 2] -> ["two", 2, 1] -> ["three", 3, 0]
const sequence: Seq<[string, number]> = zip3(seq1, seq2, seq3)
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type zip3 = <T1, T2, T3>(
  seq1: Seq<T1>,
  seq2: Seq<T2>,
  seq3: Seq<T3>,
) => Seq<[T1 | undefined, T2 | undefined, T3 | undefined]>
```

{% endtab %}
{% endtabs %}

## zip3With

Takes three sequences and lazily combines them to produce an arbitrary value by mapping the current value of the three positions through a user-supplied function.

{% tabs %}
{% tab title="Usage" %}

```typescript
const seq1 = range(0, 3)
const seq2 = repeat(2)
const seq3 = repeat(1)

// Gives: 0 -> 2 -> 4 -> 6
const sequence: Seq<number> = zip3With(
  ([num, multiplier, divisor]) => (num * multiplier) / divisor,
  seq1,
  seq2,
  seq3,
)
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type zip3With = <T1, T2, T3, T4>(
  fn: (
    [result1, result2, resul3]:
      | [T1, T2, T3]
      | [T1, undefined, undefined]
      | [T1, T2, undefined]
      | [T1, undefined, T3]
      | [undefined, T2, undefined]
      | [undefined, T2, T3]
      | [undefined, undefined, T3],
    index: number,
  ) => T4,
  seq1: Seq<T1>,
  seq2: Seq<T2>,
  seq3: Seq<T3>,
) => Seq<T4>
```

{% endtab %}
{% endtabs %}

## concat

Combines 2 or more sequences into a single sequence.

{% tabs %}
{% tab title="Usage" %}

```typescript
const sequence: Seq<number> = concat(
  fromArray([0, 1]),
  fromArray([2, 3]),
  fromArray([4, 5]),
)
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type concat = <T>(...items: Array<Seq<T>>) => Seq<T>
```

{% endtab %}
{% endtabs %}

## interleave

Takes 2 or more sequences and creates a new sequence built by pulling the next value from each of the sequences in order.

{% tabs %}
{% tab title="Usage" %}

```typescript
// Builds: a -> 1 -> b -> 2 -> c -> 3
const sequence: Seq<string | number> = interleave(
  fromArray(["a", "b", "c"]),
  range(1, 3),
)
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type interleave = <T>(...items: Array<Seq<T>>) => Seq<T>
```

{% endtab %}
{% endtabs %}


# Instance Methods

## map

It's `map`, the most important method in programming! Exactly the same as ES6 `map`, but lazy.

{% tabs %}
{% tab title="Usage" %}

```typescript
const sequence: Seq<string> = Seq.infinite().map(num => num.toString())
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type map = <U>(fn: (value: T, index: number) => U) => Seq<U>
```

{% endtab %}
{% endtabs %}

## window

`window` takes a sequence and groups it into "windows" of a certain length. This works well with infinite sequences where you want to process some number of values at a time.

{% tabs %}
{% tab title="Usage" %}

```typescript
// Grab numbers in groups of 10.
const sequence: Seq<number[]> = Seq.infinite().window(10)
```

By default, only triggers chained responses when the window fills, guaranteeing the window is the exact size expect. Set `allowPartialWindow` to `false` to allow the trailing edge of a sequence to not be divisible by the window size.

```typescript
// Gives: [0, 1, 2] -> [3, 4, 5] -> [6, 7, 8] -> [9, 10]
const sequence: Seq<number> = Seq.range(0, 10).window(3)
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type window = (size: number, allowPartialWindow = true) => Seq<T[]>
```

{% endtab %}
{% endtabs %}

## pairwise

Works like `window`, makes the window size 2. Groups a sequence as alternating pairs. Useful for processing data which alternates Map keys and values.

{% tabs %}
{% tab title="Usage" %}

```typescript
const sequence: Seq<[string, number]> = Seq.fromArray(["a", 1, "b", 2])
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type pairwise = () => Seq<[T, T]>
```

{% endtab %}
{% endtabs %}

## isEmpty

Ask whether a sequence is empty.

{% tabs %}
{% tab title="Usage" %}

```typescript
const anythingInThere: boolean = Seq.empty().isEmpty()
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type isEmpty = () => boolean
```

{% endtab %}
{% endtabs %}

## tap

`tap` lets you run side-effect generating functions on a sequence. Allows you to "tap in to" a data flow. Very useful for logging and debugging what values are flowing through the chain at a given location.

{% tabs %}
{% tab title="Usage" %}

```typescript
const sequence: Seq<number> = Seq.infinite().tap(num => console.log(num))
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type tap = (fn: (value: T, index: number) => void) => Seq<T>
```

{% endtab %}
{% endtabs %}

## log

`log` provides the most common use-case for \`tap. Add this to a sequence chain to log each value that passes through it.

{% tabs %}
{% tab title="Usage" %}

```typescript
const sequence: Seq<number> = Seq.infinite().log()
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type log = () => Seq<T>
```

{% endtab %}
{% endtabs %}

## flat

Given a sequence where each item in an array of items, flatten all those arrays into a single flat sequence of values.

Works just like `Array.prototype.flat`. [See more here.](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat)

{% tabs %}
{% tab title="Usage" %}

```typescript
type Person = { name: string; friends: Person[] }

const sequence: Seq<Friend> = Seq.fromArray([person1, person2])
  .map(person => person.friends)
  .flat()
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type flat = <U>(this: Seq<U[]>) => Seq<U>
```

{% endtab %}
{% endtabs %}

## flatMap

`flatMap` is used when mapping a list to each items related items. For example, if you wanted to map from a list of people to each persons list of friends. Despite each mapping function returning an array, the final output is a flatten array of all the results concattenated.

Works just like `Array.prototype.flatMap`. [See more here.](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flatMap)

Similar to `[].map().flat()`, but in `leisure` the item mappings won't execute until enough of the resulting values have been realized to trigger each map.

{% tabs %}
{% tab title="Usage" %}

```typescript
type Person = { name: string; friends: Person[] }

const sequence: Seq<Friend> = Seq.fromArray([person1, person2]).flatMap(
  person => person.friends,
)
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type flatMap = <U>(fn: (value: T, index: number) => U[]) => Seq<U>
```

{% endtab %}
{% endtabs %}

## filter

Runs a predicate function on each item in a sequence to produce a new sequence where only the values which responded with `true` remain.

Exactly the same as `Array.prototype.filter`, but lazy. [See more here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter).

{% tabs %}
{% tab title="Usage" %}

```typescript
// Create a sequence of only even numbers.
const sequence: Seq<number> = Seq.infinite().filter(num => num % 2 === 0)
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type filter = (fn: (value: T, index: number) => unknown) => Seq<T>
```

{% endtab %}
{% endtabs %}

## concat

Combines the current sequence with 1 or more additional sequences.

Exactly the same as `Array.prototype.concat`, but lazy. [See more here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat).

{% tabs %}
{% tab title="Usage" %}

```typescript
const sequence: Seq<number> = Seq.fromArray([0, 1]).concat(
  Seq.fromArray([2, 3]),
  Seq.fromArray([4, 5]),
)
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type concat = (...tail: Array<Seq<T>>) => Seq<T>
```

{% endtab %}
{% endtabs %}

## interleave

Takes 1 or more sequences and creates a new sequence built by pulling the next value from each of the sequences in order.

{% tabs %}
{% tab title="Usage" %}

```typescript
// Builds: a -> 1 -> b -> 2 -> c -> 3
const sequence: Seq<string | number> = Seq.fromArray([
  "a",
  "b",
  "c",
]).interleave(Seq.range(1, 3))
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type interleave = (...tail: Array<Seq<T>>) => Seq<T>
```

{% endtab %}
{% endtabs %}

## interpose

Given a sequence, place a value between each value of the original sequence. Useful for adding punctuation between strings.

{% tabs %}
{% tab title="Usage" %}

```typescript
// Builds: Apples -> , -> Oranges -> , -> Bananas
const sequence: Seq<string> = Seq.fromArray([
  "Apples",
  "Oranges",
  "Bananas",
]).interpose(", ")

console.log(sequence.toArray().join(""))
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type interpose = (separator: T) => Seq<T>
```

{% endtab %}
{% endtabs %}

## distinct

Given a sequence, only forwards the values which have no already been seen. Very similar to lodash's `uniq` method.

{% tabs %}
{% tab title="Usage" %}

```typescript
// Builds: 1 -> 2 -> 3 -> 4
const sequence: Seq<number> = Seq.fromArray([1, 2, 3, 2, 1, 4]).distinct()
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type distinct = () => Seq<T>
```

{% endtab %}
{% endtabs %}

## distinctBy

Same as `distinct`, but allows a function to describe on what value the sequence should be unique.

{% tabs %}
{% tab title="Usage" %}

```typescript
// Builds: { firstName: "A", lastName: "Z" } ->
//         { firstName: "B", lastName: "Y" } ->
//         { firstName: "C", lastName: "W" }
type Person = { firstName: string; lastName: string }
const sequence: Seq<Person> = Seq.fromArray([
  { firstName: "A", lastName: "Z" },
  { firstName: "B", lastName: "Y" },
  { firstName: "A", lastName: "X" },
  { firstName: "C", lastName: "W" },
]).distinctBy(person => person.firstName)
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type distinctBy = <U>(fn: (value: T) => U) => Seq<T>
```

{% endtab %}
{% endtabs %}

## partitionBy

Given a sequence, splits the values into two separate sequences. One represents the values where the partition function is `true` and the other for `false`.

{% tabs %}
{% tab title="Usage" %}

```typescript
const [isEven, isOdd] = Seq.infinite().partitionBy(num => num % 2 === 0)
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type partition = (fn: (value: T, index: number) => unknown) => [Seq<T>, Seq<T>]
```

{% endtab %}
{% endtabs %}

## includes

Lazily checks if the sequence includes a value.

Exactly the same as `Array.prototype.includes`, but lazy. [See more here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes).

{% tabs %}
{% tab title="Usage" %}

```typescript
const doesItInclude = Seq.infinite().includes(10)
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type includes = (value: T) => boolean
```

{% endtab %}
{% endtabs %}

## find

Lazily searches for a value that matches the predicate.

Exactly the same as `Array.prototype.find`, but lazy. [See more here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find).

{% tabs %}
{% tab title="Usage" %}

```typescript
// Returns 11
const gtTen = Seq.infinite().find(num => num > 10)
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type find = (fn: (value: T, index: number) => unknown) => T | undefined
```

{% endtab %}
{% endtabs %}

## reduce

Exactly the same as `Array.prototype.reduce`. [See more here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce). This causes a full realization of the data. Not lazy.

{% tabs %}
{% tab title="Usage" %}

```typescript
// Returns 0 + 1 + 2 + 3 + 4 = 10
const sum = Seq.infinite()
  .take(5)
  .reduce((sum, num) => sum + num)
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type reduce = <A>(fn: (sum: A, value: T, index: number) => A, initial: A) => A
```

{% endtab %}
{% endtabs %}

## chain

This method is helpful for chaining. Shocking, I know. Let's you "map" the entire sequence in a chain, rather than per-each-item. Allows adding arbitrary sequence helpers and methods to chain, even if they are written in user-land and not on the `Seq` prototype.

{% tabs %}
{% tab title="Usage" %}

```typescript
// Same as `Seq.interpose(Seq.infinite(), Seq.infinite())`
const sequence = Seq.infinite().chain(seq => seq.interpose(Seq.infinite()))
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type chain = <U>(fn: (value: Seq<T>) => U) => U
```

{% endtab %}
{% endtabs %}

## some

Exactly the same as `Array.prototype.some`, but lazy. [See more here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some).

{% tabs %}
{% tab title="Usage" %}

```typescript
// Find the first even random number.
const areAnyEven = Seq.random()
  .map(num => Math.round(num * 1000))
  .some(num => num % 2 === 0)
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type some = (fn: (value: T, index: number) => unknown) => boolean
```

{% endtab %}
{% endtabs %}

## every

Exactly the same as `Array.prototype.every`, but lazy. [See more here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every).

{% tabs %}
{% tab title="Usage" %}

```typescript
// Fails fast if there are negative numbers
const areAllPositive = Seq.random()
  .map(num => Math.round(num * 1000) - 500)
  .every(num => num > 0)
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type every = (fn: (value: T, index: number) => unknown) => boolean
```

{% endtab %}
{% endtabs %}

## take

Given a sequence of unknown length, create a sub sequence of just the first X number of items.

{% tabs %}
{% tab title="Usage" %}

```typescript
// Grabs 0 -> 1 -> 2 -> 3 -> 4
const firstFive = Seq.infinite().take(5)
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type take = (num: number) => Seq<T>
```

{% endtab %}
{% endtabs %}

## takeWhile

Given a sequence of unknown length, create a sub sequence of as many items in a row that satisfy the predicate.

{% tabs %}
{% tab title="Usage" %}

```typescript
// Gives 0 -> 1 -> 2 -> 3 -> 4
const lessThanFive = Seq.infinite().takeWhile(num => num < 5)
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type takeWhile = (fn: (value: T, index: number) => unknown) => Seq<T>
```

{% endtab %}
{% endtabs %}

## skip

Given a sequence of unknown length, skips the first X number of items.

{% tabs %}
{% tab title="Usage" %}

```typescript
// Gives 5 -> 6 -> 7 -> 8 -> 9
const secondFive = Seq.infinite().skip(5).take(5)
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type skip = (num: number) => Seq<T>
```

{% endtab %}
{% endtabs %}

## skipWhile

Given a sequence of unknown length, skip as many items in a row that satisfy the predicate.

{% tabs %}
{% tab title="Usage" %}

```typescript
// Gives 5 -> 6 -> 7 -> 8 -> 9
const greaterThanFive = Seq.infinite()
  .skipWhile(num => num < 5)
  .take(5)
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type skipWhile = (fn: (value: T, index: number) => unknown) => Seq<T>
```

{% endtab %}
{% endtabs %}

## nth

Returns the `nth` item. Items are 1-indexed.

{% tabs %}
{% tab title="Usage" %}

```typescript
const thirdItem = Seq.infinite().nth(3)
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type nth = (i: number) => T | undefined
```

{% endtab %}
{% endtabs %}

## index

Returns the `index` item. Items are 0-indexed.

{% tabs %}
{% tab title="Usage" %}

```typescript
const fourthItem = Seq.infinite().index(3)
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type nth = (i: number) => T | undefined
```

{% endtab %}
{% endtabs %}

## first

Gets the first value in the sequence.

{% tabs %}
{% tab title="Usage" %}

```typescript
const fifth = Seq.infinite().skip(4).first()
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type first = () => T | undefined
```

{% endtab %}
{% endtabs %}

## zip

Lazily combines a second sequence with this current one to produce a tuple with the current step in each of the two positions. Useful for zipping a sequence of keys with a sequence of values, before converting to a Map of key to value.

{% tabs %}
{% tab title="Usage" %}

```typescript
const seq2 = Seq.range(0, 3)

// Gives: ["zero", 0] -> ["one", 1] -> ["two", 2] -> ["three", 3]
const sequence: Seq<[string, number]> = Seq.fromArray([
  "zero",
  "one",
  "two",
  "three",
]).zip(seq2)
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type zip<T2> = (seq2: Seq<T2>) => Seq<[T | undefined, T2 | undefined]>
```

{% endtab %}
{% endtabs %}

## zipWith

Takes a second sequence and lazily combines it to produce an arbitrary value by mapping the current value of the two positions through a user-supplied function. Useful for table (row/col) math.

{% tabs %}
{% tab title="Usage" %}

```typescript
const seq2 = Seq.repeat(2)

// Gives: 0 -> 2 -> 4 -> 6
const sequence: Seq<number> = Seq.range(0, 3).zipWith(
  ([num, multiplier]) => num * multiplier,
  seq2,
)
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type zip2With = <T2, T3, T4>(
  fn: (
    [result1, result2, result3]:
      | [T, T2, T3]
      | [T, undefined, undefined]
      | [T, T2, undefined]
      | [T, undefined, T3]
      | [undefined, T2, undefined]
      | [undefined, T2, T3]
      | [undefined, undefined, T3],
    index: number,
  ) => T4,
  seq2: Seq<T2>,
  seq3: Seq<T3>,
) => Seq<T4>
```

{% endtab %}
{% endtabs %}

## zip2

Takes two sequences and lazily combines them with this one to produce a 3-tuple with the current step in each of the three positions.

{% tabs %}
{% tab title="Usage" %}

```typescript
const seq2 = Seq.range(0, 3)
const seq3 = Seq.range(3, 0)

// Gives: ["zero", 0, 3] -> ["one", 1, 2] -> ["two", 2, 1] -> ["three", 3, 0]
const sequence: Seq<[string, number]> = Seq.fromArray([
  "zero",
  "one",
  "two",
  "three",
]).zip2(seq2, seq3)
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type zip2 = <T2, T3>(
  seq2: Seq<T2>,
  seq3: Seq<T3>,
) => Seq<[T | undefined, T2 | undefined, T3 | undefined]>
```

{% endtab %}
{% endtabs %}

## zip2With

Takes two sequences and lazily combine them with this sequence to produce an arbitrary value by mapping the current value of the three positions through a user-supplied function.

{% tabs %}
{% tab title="Usage" %}

```typescript
const seq2 = Seq.repeat(2)
const seq3 = Seq.repeat(1)

// Gives: 0 -> 2 -> 4 -> 6
const sequence: Seq<number> = Seq.range(0, 3).zip2With(
  ([num, multiplier, divisor]) => (num * multiplier) / divisor,
  seq2,
  seq3,
)
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type zip2With = <T2, T3, T4>(
  fn: (
    [result1, result2, result3]:
      | [T, T2, T3]
      | [T, undefined, undefined]
      | [T, T2, undefined]
      | [T, undefined, T3]
      | [undefined, T2, undefined]
      | [undefined, T2, T3]
      | [undefined, undefined, T3],
    index: number,
  ) => T4,
  seq2: Seq<T2>,
  seq3: Seq<T3>,
) => Seq<T4>
```

{% endtab %}
{% endtabs %}

## toArray

Converts the sequence to a real JavaScript array. Realizes the entire sequence.

{% tabs %}
{% tab title="Usage" %}

```typescript
const lessThanTen = Seq.infinite().take(10).toArray()
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type toArray = () => T[]
```

{% endtab %}
{% endtabs %}

## forEach

Works just like `Array.prototype.forEach`. [See more here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach). Realizes the full sequence.

{% tabs %}
{% tab title="Usage" %}

```typescript
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type forEach = (fn: (value: T, index: number) => void) => void
```

{% endtab %}
{% endtabs %}

## sum

Given a sequence of numbers, adds them all together. This realizes the entire sequence.

{% tabs %}
{% tab title="Usage" %}

```typescript
// Returns 0 + 1 + 2 + 3 + 4 = 10
const sum = Seq.infinite().take(5).sum()
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type sum = (this: Seq<number>) => number
```

{% endtab %}
{% endtabs %}

## sumBy

Given a sequence of arbitrary data, adds together the result of the mapping function. This realizes the entire sequence.

{% tabs %}
{% tab title="Usage" %}

```typescript
// Returns 0 + 1 + 2 + 3 + 4 = 10
const sum = Seq.fromArray([
  { balance: 0 },
  { balance: 1 },
  { balance: 2 },
  { balance: 3 },
  { balance: 4 },
]).sumBy(user => user.balance)
```

````

</div>

<div data-gb-custom-block data-tag="tab" data-title='Type Definition'>

```typescript
type sumBy = (fn: (value: T) => number) => number;
````

{% endtab %}
{% endtabs %}

## average

Given a sequence of numbers, averages them all together. Tise realizes the entire sequence.

{% tabs %}
{% tab title="Usage" %}

```typescript
// Returns (0 + 1 + 2 + 3 + 4) / 5 = 2
const sum = Seq.infinite().take(5).average()
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type average = (this: Seq<number>) => number
```

{% endtab %}
{% endtabs %}

## averageBy

Given a sequence of arbitrary data, averages together the result of the mapping function. This realizes the entire sequence.

{% tabs %}
{% tab title="Usage" %}

```typescript
// Returns (0 + 1 + 2 + 3 + 4) / 5 = 2
const sum = Seq.fromArray([
  { balance: 0 },
  { balance: 1 },
  { balance: 2 },
  { balance: 3 },
  { balance: 4 },
]).averageBy(user => user.balance)
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type averageBy = (fn: (value: T) => number) => number
```

{% endtab %}
{% endtabs %}

## frequencies

Given a non-infinite sequence, return a `Map` which counts the occurances of each unique value. This realizes the entire sequence.

{% tabs %}
{% tab title="Usage" %}

```typescript
// Returns a Map of numbers from 0->100 and how many times they randomly occured in this set of 500.
const freq = Seq.random()
  .map(num => Math.round(num * 100))
  .take(500)
  .frequencies()
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type frequencies = () => Map<T, number>
```

{% endtab %}
{% endtabs %}

## groupBy

Group a sequence by the return of a mapping function. This realizes the entire sequence.

{% tabs %}
{% tab title="Usage" %}

```typescript
// Random generates 1000 years between 0-2000 and
// groups them by decade.
const groupedByDecade = Seq.random()
  .map(num => Math.round(num * 2000))
  .take(100)
  .groupBy(year => Math.round(year / 10))
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type groupBy = <U>(fn: (item: T) => U) => Map<U, T[]>
```

{% endtab %}
{% endtabs %}


# Simplex Methods

These methods create sequences of simplex noise.

## simplex2D

Generates a 2d simplex noise.

{% tabs %}
{% tab title="Usage" %}

```typescript
const SEED = 5
const sequence: Seq<number> = simplex2D((x, y) => x + y, SEED)
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type simplex2D = (
  fn: () => [number, number],
  seed: number = Date.now(),
) => Seq<number>
```

{% endtab %}
{% endtabs %}

## simplex3D

Generates a 3d simplex noise.

{% tabs %}
{% tab title="Usage" %}

```typescript
const SEED = 5
const sequence: Seq<number> = simplex3D((x, y, z) => x + y + z, SEED)
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type simplex3D = (
  fn: () => [number, number, number],
  seed: number = Date.now(),
) => Seq<number>
```

{% endtab %}
{% endtabs %}

## simplex4D

Generates a 4d simplex noise.

{% tabs %}
{% tab title="Usage" %}

```typescript
const SEED = 5
const sequence: Seq<number> = simplex4D((x, y, z, w) => x + y + z + w, SEED)
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type simplex4D = (
  fn: () => [number, number, number, number],
  seed: number = Date.now(),
) => Seq<number>
```

{% endtab %}
{% endtabs %}


# Combinatorial Methods

These methods create sequences of the very large combinations of values.

## cartesianProduct

Generates sequence of the [Cartesian product](https://en.wikipedia.org/wiki/Cartesian_product) of an arbitrary number of columns of data.

{% tabs %}
{% tab title="Usage" %}

```typescript
const sequence: Seq<[string, number]> = cartesianProduct(
  ["a", "b", "c"],
  [1, 2, 3],
)
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type cartesianProduct = <T>(...inputs: T[][]) => Seq<T[]>
```

{% endtab %}
{% endtabs %}

## powerSet

Generates sequence of Sets of a [Power set](https://en.wikipedia.org/wiki/Power_set).

{% tabs %}
{% tab title="Usage" %}

```typescript
const sequence: Seq<Set<string>> = powerSet(["a", "b", "c"])
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type powerSet = <T>(...items: T[]) => Seq<Set<T>>
```

{% endtab %}
{% endtabs %}

## combination

Generates sequence of Sets of a [Combination](https://en.wikipedia.org/wiki/Combination) of a given length.

{% tabs %}
{% tab title="Usage" %}

```typescript
const sequence: Seq<Set<string>> = combination(["a", "b", "c"], 2)
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type combination = <T>(items: T[], size: number = items.length) => Seq<Set<T>>
```

{% endtab %}
{% endtabs %}


# Random Number Generating Methods

These methods create reproducible sequences of random numbers given an initial seed value.

This methods rely on the [pure-rand](https://github.com/dubzzz/pure-rand) project.

## random

Generates random numbers using the Mersenne Twister generator whose values are within the range 0 to 0xffffffff.

{% tabs %}
{% tab title="Usage" %}

```typescript
const SEED = 5
const sequence: Seq<number> = random(SEED)
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type random = (seed = DEFAULT_SEED) => Seq<number>
```

{% endtab %}
{% endtabs %}

## mersenne

Generates random numbers using the Mersenne Twister generator whose values are within the range 0 to 0xffffffff.

{% tabs %}
{% tab title="Usage" %}

```typescript
const SEED = 5
const sequence: Seq<number> = mersenne(SEED)
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type mersenne = (seed = DEFAULT_SEED) => Seq<number>
```

{% endtab %}
{% endtabs %}

## xorshift128plus

Generates random numbers using the xorshift128+ generator whose values are within the range -0x80000000 to 0x7fffffff.

{% tabs %}
{% tab title="Usage" %}

```typescript
const SEED = 5
const sequence: Seq<number> = xorshift128plus(SEED)
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type xorshift128plus = (seed = DEFAULT_SEED) => Seq<number>
```

{% endtab %}
{% endtabs %}

## xoroshiro128plus

Generates random numbers using the xoroshiro128+ generator whose values are within the range -0x80000000 to 0x7fffffff.

{% tabs %}
{% tab title="Usage" %}

```typescript
const SEED = 5
const sequence: Seq<number> = xoroshiro128plus(SEED)
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type xoroshiro128plus = (seed = DEFAULT_SEED) => Seq<number>
```

{% endtab %}
{% endtabs %}

## congruential

Generates random numbers using a Linear Congruential generator whose values are within the range 0 to 0x7fff.

{% tabs %}
{% tab title="Usage" %}

```typescript
const SEED = 5
const sequence: Seq<number> = congruential(SEED)
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type congruential = (seed = DEFAULT_SEED) => Seq<number>
```

{% endtab %}
{% endtabs %}

## congruential32

Generates random numbers using a Linear Congruential generator whose values are within the range 0 to 0xffffffff.

{% tabs %}
{% tab title="Usage" %}

```typescript
const SEED = 5
const sequence: Seq<number> = congruential32(SEED)
```

{% endtab %}

{% tab title="Type Definition" %}

```typescript
type congruential32 = (seed = DEFAULT_SEED) => Seq<number>
```

{% endtab %}
{% endtabs %}


