diff --git a/index.ts b/index.ts index 0792fcf..a0b96a1 100644 --- a/index.ts +++ b/index.ts @@ -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' diff --git a/sequences/generateSequence.test.ts b/sequences/generateSequence.test.ts new file mode 100644 index 0000000..d4700f5 --- /dev/null +++ b/sequences/generateSequence.test.ts @@ -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* (iterable: Iterable): Iterable { + if (n > 0) { + let i = 1 + for (const element of iterable) { + yield element + if (++i > n) break + } + } +} diff --git a/sequences/generateSequence.ts b/sequences/generateSequence.ts new file mode 100644 index 0000000..8cbe5d0 --- /dev/null +++ b/sequences/generateSequence.ts @@ -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>(seed: T, next: (c: T) => T | undefined | null): Iterable { + 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 = >(seed: T, next: (c: T) => T | undefined | null): Seq => + * Seq(generateSequence(seed, next)) + */ diff --git a/sequences/index.ts b/sequences/index.ts new file mode 100644 index 0000000..640f971 --- /dev/null +++ b/sequences/index.ts @@ -0,0 +1 @@ +export * from './generateSequence.js'