Skip to content

Commit

Permalink
add generate sequence
Browse files Browse the repository at this point in the history
  • Loading branch information
andrej-dyck committed Dec 16, 2023
1 parent e04a2d0 commit c53253a
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 0 deletions.
1 change: 1 addition & 0 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ export * from './locale/index.js'
export * from './nullables/index.js'
export * from './raise/index.js'
export * from './records/index.js'
export * from './sequences/index.js'
33 changes: 33 additions & 0 deletions sequences/generateSequence.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { expect, test } from 'vitest'
import { generateSequence } from './index.js'

test('generateSequence has only seed when next is null', () => {
expect([...generateSequence(1, () => null)]).toEqual([1])
})

test('generateSequence has only seed when next is undefined', () => {
expect([...generateSequence(1, () => undefined)]).toEqual([1])
})

test('generateSequence builds a sequence until next returns nullish value', () => {
expect([...generateSequence(1, n => n < 10 ? n + 1 : null)]).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
})

test('generateSequence is lazy', () => {
const seq = generateSequence(1, n => {
if (n < 10) return n + 1
throw new Error()
})

expect([...take(3)(seq)]).toEqual([1, 2, 3])
})

const take = (n: number) => function* <T>(iterable: Iterable<T>): Iterable<T> {
if (n > 0) {
let i = 1
for (const element of iterable) {
yield element
if (++i > n) break
}
}
}
26 changes: 26 additions & 0 deletions sequences/generateSequence.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* Generates a (possibly infinite) sequence of values using a seed and a next function.
* Stops when next() returns null or undefined.
*/
export function* generateSequence<T extends NonNullable<unknown>>(seed: T, next: (c: T) => T | undefined | null): Iterable<T> {
let n: T | undefined | null = seed
while (n != null) {
yield n
n = next(n)
}
}

/*
* Examples:
* const naturalNumbers = generateSequence(1, n => n + 1) // 1, 2, 3, 4, 5, ...
* const gameOfLife = generateSequence(board, b => b.applyRules(rules))
*/

/*
* Can be used well with ImmutableJS Seq (https://immutable-js.com/#lazy-seq); e.g.,
* const odds = Seq(naturalNumbers).take(100).filter(n => n % 2 !== 0)
*
* // or a constructor function
* const generateSeq = <T extends NonNullable<unknown>>(seed: T, next: (c: T) => T | undefined | null): Seq<number, T> =>
* Seq(generateSequence(seed, next))
*/
1 change: 1 addition & 0 deletions sequences/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './generateSequence.js'

0 comments on commit c53253a

Please sign in to comment.