diff --git a/packages/it-filter/src/index.ts b/packages/it-filter/src/index.ts index a4b84ec4..6126c616 100644 --- a/packages/it-filter/src/index.ts +++ b/packages/it-filter/src/index.ts @@ -12,7 +12,7 @@ * // This can also be an iterator, generator, etc * const values = [0, 1, 2, 3, 4] * - * const fn = val => val > 2 // Return boolean to keep item + * const fn = (val, index) => val > 2 // Return boolean to keep item * * const arr = all(filter(values, fn)) * @@ -29,7 +29,7 @@ * yield * [0, 1, 2, 3, 4] * } * - * const fn = async val => val > 2 // Return boolean or promise of boolean to keep item + * const fn = async val => (val, index) > 2 // Return boolean or promise of boolean to keep item * * const arr = await all(filter(values, fn)) * @@ -46,14 +46,16 @@ function isAsyncIterable (thing: any): thing is AsyncIterable { /** * Filters the passed (async) iterable by using the filter function */ -function filter (source: Iterable, fn: (val: T) => Promise): AsyncGenerator -function filter (source: Iterable, fn: (val: T) => boolean): Generator -function filter (source: Iterable | AsyncIterable, fn: (val: T) => boolean | Promise): AsyncGenerator -function filter (source: Iterable | AsyncIterable, fn: (val: T) => boolean | Promise): Generator | AsyncGenerator { +function filter (source: Iterable, fn: (val: T, index: number) => Promise): AsyncGenerator +function filter (source: Iterable, fn: (val: T, index: number) => boolean): Generator +function filter (source: Iterable | AsyncIterable, fn: (val: T, index: number) => boolean | Promise): AsyncGenerator +function filter (source: Iterable | AsyncIterable, fn: (val: T, index: number) => boolean | Promise): Generator | AsyncGenerator { + let index = 0 + if (isAsyncIterable(source)) { return (async function * () { for await (const entry of source) { - if (await fn(entry)) { + if (await fn(entry, index++)) { yield entry } } @@ -68,7 +70,7 @@ function filter (source: Iterable | AsyncIterable, fn: (val: T) => boo return (function * () {}()) } - const res = fn(value) + const res = fn(value, index++) // @ts-expect-error .then is not present on O if (typeof res.then === 'function') { @@ -78,14 +80,14 @@ function filter (source: Iterable | AsyncIterable, fn: (val: T) => boo } for await (const entry of peekable) { - if (await fn(entry)) { + if (await fn(entry, index++)) { yield entry } } })() } - const func = fn as (val: T) => boolean + const func = fn as (val: T, index: number) => boolean return (function * () { if (res === true) { @@ -93,7 +95,7 @@ function filter (source: Iterable | AsyncIterable, fn: (val: T) => boo } for (const entry of peekable) { - if (func(entry)) { + if (func(entry, index++)) { yield entry } } diff --git a/packages/it-filter/test/index.spec.ts b/packages/it-filter/test/index.spec.ts index 3c7787b7..4b36f90c 100644 --- a/packages/it-filter/test/index.spec.ts +++ b/packages/it-filter/test/index.spec.ts @@ -2,12 +2,12 @@ import { expect } from 'aegir/chai' import all from 'it-all' import filter from '../src/index.js' -function * values (): Generator { - yield * [0, 1, 2, 3, 4] +function * values (vals: number[] = [0, 1, 2, 3, 4]): Generator { + yield * vals } -async function * asyncValues (): AsyncGenerator { - yield * values() +async function * asyncValues (vals: number[] = [0, 1, 2, 3, 4]): AsyncGenerator { + yield * values(vals) } describe('it-filter', () => { @@ -52,4 +52,42 @@ describe('it-filter', () => { expect(res[Symbol.asyncIterator]).to.be.ok() await expect(all(res)).to.eventually.deep.equal([3, 4]) }) + + it('should filter values with indexes', async () => { + const vals = [4, 3, 2, 1, 0] + const callbackArgs: any[] = [] + const gen = filter(values(vals), (...args: any[]) => { + callbackArgs.push(args) + return true + }) + expect(gen[Symbol.iterator]).to.be.ok() + + const results = all(gen) + expect(results).to.have.lengthOf(vals.length) + expect(callbackArgs).to.have.lengthOf(vals.length) + + vals.forEach((value, index) => { + expect(callbackArgs).to.have.nested.property(`[${index}][0]`, value) + expect(callbackArgs).to.have.nested.property(`[${index}][1]`, index) + }) + }) + + it('should filter async values with indexes', async () => { + const vals = [4, 3, 2, 1, 0] + const callbackArgs: any[] = [] + const gen = filter(asyncValues(vals), (...args: any[]) => { + callbackArgs.push(args) + return true + }) + expect(gen[Symbol.asyncIterator]).to.be.ok() + + const results = await all(gen) + expect(results).to.have.lengthOf(vals.length) + expect(callbackArgs).to.have.lengthOf(vals.length) + + vals.forEach((value, index) => { + expect(callbackArgs).to.have.nested.property(`[${index}][0]`, value) + expect(callbackArgs).to.have.nested.property(`[${index}][1]`, index) + }) + }) }) diff --git a/packages/it-foreach/src/index.ts b/packages/it-foreach/src/index.ts index 35b96547..fbce6125 100644 --- a/packages/it-foreach/src/index.ts +++ b/packages/it-foreach/src/index.ts @@ -16,7 +16,7 @@ * // This can also be an iterator, generator, etc * const values = [0, 1, 2, 3, 4] * - * // prints 0, 1, 2, 3, 4 + * // prints [0, 0], [1, 1], [2, 2], [3, 3], [4, 4] * const arr = drain( * each(values, console.info) * ) @@ -32,7 +32,7 @@ * yield * [0, 1, 2, 3, 4] * } * - * // prints 0, 1, 2, 3, 4 + * // prints [0, 0], [1, 1], [2, 2], [3, 3], [4, 4] * const arr = await drain( * each(values(), console.info) * ) @@ -52,14 +52,16 @@ function isPromise (thing: any): thing is Promise { /** * Invokes the passed function for each item in an iterable */ -function forEach (source: Iterable, fn: (thing: T) => Promise): AsyncGenerator -function forEach (source: Iterable, fn: (thing: T) => void): Generator -function forEach (source: Iterable | AsyncIterable, fn: (thing: T) => void | Promise): AsyncGenerator -function forEach (source: Iterable | AsyncIterable, fn: (thing: T) => void | Promise): AsyncGenerator | Generator { +function forEach (source: Iterable, fn: (thing: T, index: number) => Promise): AsyncGenerator +function forEach (source: Iterable, fn: (thing: T, index: number) => void): Generator +function forEach (source: Iterable | AsyncIterable, fn: (thing: T, index: number) => void | Promise): AsyncGenerator +function forEach (source: Iterable | AsyncIterable, fn: (thing: T, index: number) => void | Promise): AsyncGenerator | Generator { + let index = 0 + if (isAsyncIterable(source)) { return (async function * () { for await (const val of source) { - const res = fn(val) + const res = fn(val, index++) if (isPromise(res)) { await res @@ -78,14 +80,14 @@ function forEach (source: Iterable | AsyncIterable, fn: (thing: T) => return (function * () {}()) } - const res = fn(value) + const res = fn(value, index++) if (typeof res?.then === 'function') { return (async function * () { yield value for await (const val of peekable) { - const res = fn(val) + const res = fn(val, index++) if (isPromise(res)) { await res @@ -96,13 +98,13 @@ function forEach (source: Iterable | AsyncIterable, fn: (thing: T) => })() } - const func = fn as (val: T) => void + const func = fn as (val: T, index: number) => void return (function * () { yield value for (const val of peekable) { - func(val) + func(val, index++) yield val } })() diff --git a/packages/it-foreach/test/index.spec.ts b/packages/it-foreach/test/index.spec.ts index 12dce904..225ec73d 100644 --- a/packages/it-foreach/test/index.spec.ts +++ b/packages/it-foreach/test/index.spec.ts @@ -2,12 +2,20 @@ import { expect } from 'aegir/chai' import all from 'it-all' import forEach from '../src/index.js' +function * values (vals: number[] = [0, 1, 2, 3, 4]): Generator { + yield * vals +} + +async function * asyncValues (vals: number[] = [0, 1, 2, 3, 4]): AsyncGenerator { + yield * values(vals) +} + describe('it-for-each', () => { it('should iterate over every value', () => { - const values = [0, 1, 2, 3, 4] + const vals = [0, 1, 2, 3, 4] let sum = 0 - const gen = forEach(values, (val) => { + const gen = forEach(values(vals), (val) => { sum += val }) @@ -15,17 +23,15 @@ describe('it-for-each', () => { const res = all(gen) - expect(res).to.deep.equal(values) + expect(res).to.deep.equal(vals) expect(10).to.equal(sum) }) it('should iterate over every async value', async () => { - const values = async function * (): AsyncGenerator { - yield * [0, 1, 2, 3, 4] - } + const vals = [0, 1, 2, 3, 4] let sum = 0 - const gen = forEach(values(), (val) => { + const gen = forEach(asyncValues(vals), (val) => { sum += val }) @@ -33,15 +39,15 @@ describe('it-for-each', () => { const res = await all(gen) - expect(res).to.deep.equal(await all(values())) + expect(res).to.deep.equal(vals) expect(10).to.equal(sum) }) it('should iterate over every value asyncly', async () => { - const values = [0, 1, 2, 3, 4] + const vals = [0, 1, 2, 3, 4] let sum = 0 - const gen = forEach(values, async (val) => { + const gen = forEach(values(), async (val) => { sum += val }) @@ -49,17 +55,15 @@ describe('it-for-each', () => { const res = await all(gen) - expect(res).to.deep.equal(values) + expect(res).to.deep.equal(vals) expect(10).to.equal(sum) }) it('should iterate over every async value asyncly', async () => { - const values = async function * (): AsyncGenerator { - yield * [0, 1, 2, 3, 4] - } + const vals = [0, 1, 2, 3, 4] let sum = 0 - const gen = forEach(values(), async (val) => { + const gen = forEach(asyncValues(), async (val) => { sum += val }) @@ -67,17 +71,16 @@ describe('it-for-each', () => { const res = await all(gen) - expect(res).to.deep.equal(await all(values())) + expect(res).to.deep.equal(vals) expect(10).to.equal(sum) }) it('should abort source', () => { - const values = [0, 1, 2, 3, 4] let sum = 0 const err = new Error('wat') try { - all(forEach(values, (val) => { + all(forEach(values(), (val) => { sum += val if (val === 3) { @@ -91,4 +94,58 @@ describe('it-for-each', () => { expect(sum).to.equal(6) } }) + + it('should iterate over values with indexes', async () => { + const vals = [4, 3, 2, 1, 0] + const callbackArgs: any[] = [] + const gen = forEach(values(vals), (...args: [number]) => { + callbackArgs.push(args) + }) + expect(gen[Symbol.iterator]).to.be.ok() + + const results = all(gen) + expect(results).to.have.lengthOf(vals.length) + expect(callbackArgs).to.have.lengthOf(vals.length) + + vals.forEach((value, index) => { + expect(callbackArgs).to.have.nested.property(`[${index}][0]`, value) + expect(callbackArgs).to.have.nested.property(`[${index}][1]`, index) + }) + }) + + it('should iterate over async values with indexes', async () => { + const vals = [4, 3, 2, 1, 0] + const callbackArgs: any[] = [] + const gen = forEach(asyncValues(vals), (...args: any[]) => { + callbackArgs.push(args) + }) + expect(gen[Symbol.asyncIterator]).to.be.ok() + + const results = await all(gen) + expect(results).to.have.lengthOf(vals.length) + expect(callbackArgs).to.have.lengthOf(vals.length) + + vals.forEach((value, index) => { + expect(callbackArgs).to.have.nested.property(`[${index}][0]`, value) + expect(callbackArgs).to.have.nested.property(`[${index}][1]`, index) + }) + }) + + it('should iterate over async values with indexes asyncly', async () => { + const vals = [4, 3, 2, 1, 0] + const callbackArgs: any[] = [] + const gen = forEach(asyncValues(vals), async (...args: any[]) => { + callbackArgs.push(args) + }) + expect(gen[Symbol.asyncIterator]).to.be.ok() + + const results = await all(gen) + expect(results).to.have.lengthOf(vals.length) + expect(callbackArgs).to.have.lengthOf(vals.length) + + vals.forEach((value, index) => { + expect(callbackArgs).to.have.nested.property(`[${index}][0]`, value) + expect(callbackArgs).to.have.nested.property(`[${index}][1]`, index) + }) + }) }) diff --git a/packages/it-map/src/index.ts b/packages/it-map/src/index.ts index d8f35038..eda70252 100644 --- a/packages/it-map/src/index.ts +++ b/packages/it-map/src/index.ts @@ -11,7 +11,7 @@ * // This can also be an iterator, generator, etc * const values = [0, 1, 2, 3, 4] * - * const result = map(values, (val) => val++) + * const result = map(values, (val, index) => val++) * * console.info(result) // [1, 2, 3, 4, 5] * ``` @@ -25,7 +25,7 @@ * yield * [0, 1, 2, 3, 4] * } * - * const result = await map(values(), async (val) => val++) + * const result = await map(values(), async (val, index) => val++) * * console.info(result) // [1, 2, 3, 4, 5] * ``` @@ -41,14 +41,16 @@ function isAsyncIterable (thing: any): thing is AsyncIterable { * Takes an (async) iterable and returns one with each item mapped by the passed * function */ -function map (source: Iterable, func: (val: I) => Promise): AsyncGenerator -function map (source: Iterable, func: (val: I) => O): Generator -function map (source: AsyncIterable | Iterable, func: (val: I) => O | Promise): AsyncGenerator -function map (source: AsyncIterable | Iterable, func: (val: I) => O | Promise): AsyncGenerator | Generator { +function map (source: Iterable, func: (val: I, index: number) => Promise): AsyncGenerator +function map (source: Iterable, func: (val: I, index: number) => O): Generator +function map (source: AsyncIterable | Iterable, func: (val: I, index: number) => O | Promise): AsyncGenerator +function map (source: AsyncIterable | Iterable, func: (val: I, index: number) => O | Promise): AsyncGenerator | Generator { + let index = 0 + if (isAsyncIterable(source)) { return (async function * () { for await (const val of source) { - yield func(val) + yield func(val, index++) } })() } @@ -61,7 +63,7 @@ function map (source: AsyncIterable | Iterable, func: (val: I) => O return (function * () {}()) } - const res = func(value) + const res = func(value, index++) // @ts-expect-error .then is not present on O if (typeof res.then === 'function') { @@ -69,18 +71,18 @@ function map (source: AsyncIterable | Iterable, func: (val: I) => O yield await res for await (const val of peekable) { - yield func(val) + yield func(val, index++) } })() } - const fn = func as (val: I) => O + const fn = func as (val: I, index: number) => O return (function * () { yield res as O for (const val of peekable) { - yield fn(val) + yield fn(val, index++) } })() } diff --git a/packages/it-map/test/index.spec.ts b/packages/it-map/test/index.spec.ts index 03dd09d1..b1ba0e09 100644 --- a/packages/it-map/test/index.spec.ts +++ b/packages/it-map/test/index.spec.ts @@ -2,20 +2,20 @@ import { expect } from 'aegir/chai' import all from 'it-all' import map from '../src/index.js' -async function * asyncGenerator (): AsyncGenerator { - yield 1 +async function * asyncGenerator (vals: number[] = [1]): AsyncGenerator { + yield * vals } -function * generator (): Generator { - yield 1 +function * generator (vals: number[] = [1]): Generator { + yield * vals } -async function * source (): Generator | AsyncGenerator { - yield 1 +async function * source (vals: number[] = [1]): Generator | AsyncGenerator { + yield * vals } describe('it-map', () => { - it('should map an async iterator', async () => { + it('should map an async generator', async () => { const gen = map(asyncGenerator(), (val) => val + 1) expect(gen[Symbol.asyncIterator]).to.be.ok() @@ -24,7 +24,21 @@ describe('it-map', () => { expect(results).to.have.nested.property('[0]', 2) }) - it('should map an async iterator to a promise', async () => { + it('should map an async generator with indexes', async () => { + const vals = [4, 3, 2, 1, 0] + const gen = map(asyncGenerator(vals), (...args: any[]) => args) + expect(gen[Symbol.asyncIterator]).to.be.ok() + + const results = await all(gen) + expect(results).to.have.lengthOf(vals.length) + + vals.forEach((value, index) => { + expect(results).to.have.nested.property(`[${index}][0]`, value) + expect(results).to.have.nested.property(`[${index}][1]`, index) + }) + }) + + it('should map an async generator to a promise', async () => { const gen = map(asyncGenerator(), async (val) => val + 1) expect(gen[Symbol.asyncIterator]).to.be.ok() @@ -42,6 +56,20 @@ describe('it-map', () => { expect(results).to.have.nested.property('[0]', 2) }) + it('should map an iterator with indexes', async () => { + const vals = [4, 3, 2, 1, 0] + const gen = map(generator(vals), (...args: any[]) => args) + expect(gen[Symbol.iterator]).to.be.ok() + + const results = all(gen) + expect(results).to.have.lengthOf(vals.length) + + vals.forEach((value, index) => { + expect(results).to.have.nested.property(`[${index}][0]`, value) + expect(results).to.have.nested.property(`[${index}][1]`, index) + }) + }) + it('should map an iterator to a promise', async () => { const gen = map(generator(), async (val) => val + 1) expect(gen[Symbol.asyncIterator]).to.be.ok() @@ -59,4 +87,18 @@ describe('it-map', () => { expect(results).to.have.lengthOf(1) expect(results).to.have.nested.property('[0]', 2) }) + + it('should map a source with indexes', async () => { + const vals = [4, 3, 2, 1, 0] + const gen = map(source(vals), (...args: any[]) => args) + expect(gen[Symbol.asyncIterator]).to.be.ok() + + const results = await all(gen) + expect(results).to.have.lengthOf(vals.length) + + vals.forEach((value, index) => { + expect(results).to.have.nested.property(`[${index}][0]`, value) + expect(results).to.have.nested.property(`[${index}][1]`, index) + }) + }) }) diff --git a/packages/it-reduce/src/index.ts b/packages/it-reduce/src/index.ts index e2b7d6a4..31e7b47e 100644 --- a/packages/it-reduce/src/index.ts +++ b/packages/it-reduce/src/index.ts @@ -11,7 +11,7 @@ * // This can also be an iterator, generator, etc * const values = [0, 1, 2, 3, 4] * - * const result = reduce(values, (acc, curr) => acc + curr, 0) + * const result = reduce(values, (acc, curr, index) => acc + curr, 0) * * console.info(result) // 10 * ``` @@ -25,7 +25,7 @@ * yield * [0, 1, 2, 3, 4] * } * - * const result = await reduce(values(), (acc, curr) => acc + curr, 0) + * const result = await reduce(values(), (acc, curr, index) => acc + curr, 0) * * console.info(result) // 10 * ``` @@ -38,13 +38,15 @@ function isAsyncIterable (thing: any): thing is AsyncIterable { /** * Reduces the values yielded by an (async) iterable */ -function reduce (source: Iterable, func: (acc: V, curr: T) => V, init: V): V -function reduce (source: Iterable | AsyncIterable, func: (acc: V, curr: T) => V, init: V): Promise -function reduce (source: Iterable | AsyncIterable, func: (acc: V, curr: T) => V, init: V): Promise | V { +function reduce (source: Iterable, func: (acc: V, curr: T, index: number) => V, init: V): V +function reduce (source: Iterable | AsyncIterable, func: (acc: V, curr: T, index: number) => V, init: V): Promise +function reduce (source: Iterable | AsyncIterable, func: (acc: V, curr: T, index: number) => V, init: V): Promise | V { + let index = 0 + if (isAsyncIterable(source)) { return (async function () { for await (const val of source) { - init = func(init, val) + init = func(init, val, index++) } return init @@ -52,7 +54,7 @@ function reduce (source: Iterable | AsyncIterable, func: (acc: V, c } for (const val of source) { - init = func(init, val) + init = func(init, val, index++) } return init diff --git a/packages/it-reduce/test/index.spec.ts b/packages/it-reduce/test/index.spec.ts index cd866f54..2af79d8f 100644 --- a/packages/it-reduce/test/index.spec.ts +++ b/packages/it-reduce/test/index.spec.ts @@ -1,29 +1,59 @@ import { expect } from 'aegir/chai' import reduce from '../src/index.js' +function * values (vals: number[] = [0, 1, 2, 3, 4]): Generator { + yield * vals +} + +async function * asyncValues (vals: number[] = [0, 1, 2, 3, 4]): AsyncGenerator { + yield * values(vals) +} + describe('it-reduce', () => { it('should reduce the values yielded from an iterator', () => { - const iter = function * (): Generator { - yield 1 - yield 2 - yield 3 - } - - const result = reduce(iter(), (acc, curr) => acc + curr, 0) + const result = reduce(values(), (acc, curr) => acc + curr, 0) - expect(result).to.equal(6) + expect(result).to.equal(10) }) it('should reduce the values yielded from an async iterator', async () => { - const iter = async function * (): AsyncGenerator { - yield 1 - yield 2 - yield 3 - } - - const result = reduce(iter(), (acc, curr) => acc + curr, 0) + const result = reduce(asyncValues(), (acc, curr) => acc + curr, 0) expect(result).to.have.property('then').that.is.a('function') - await expect(result).to.eventually.equal(6) + await expect(result).to.eventually.equal(10) + }) + + it('should reduce the values yielded from an iterator with indexes', async () => { + const vals = [4, 3, 2, 1, 0] + const callbackArgs: any[] = [] + const result = reduce(values(vals), (...args: any[]) => { + callbackArgs.push(args) + return 0 + }, 0) + + expect(result).to.equal(0) + expect(callbackArgs).to.have.lengthOf(vals.length) + + vals.forEach((value, index) => { + expect(callbackArgs).to.have.nested.property(`[${index}][1]`, value) + expect(callbackArgs).to.have.nested.property(`[${index}][2]`, index) + }) + }) + + it('should reduce the values yielded from an async iterator with indexes', async () => { + const vals = [4, 3, 2, 1, 0] + const callbackArgs: any[] = [] + const result = await reduce(asyncValues(vals), (...args: any[]) => { + callbackArgs.push(args) + return 0 + }, 0) + + expect(result).to.equal(0) + expect(callbackArgs).to.have.lengthOf(vals.length) + + vals.forEach((value, index) => { + expect(callbackArgs).to.have.nested.property(`[${index}][1]`, value) + expect(callbackArgs).to.have.nested.property(`[${index}][2]`, index) + }) }) })