mirror of
https://github.com/CommunitySolidServer/CommunitySolidServer.git
synced 2024-10-03 14:55:10 +00:00
feat: Add utilities for Iterables
This commit is contained in:
parent
311f8756ec
commit
45f8aa157d
@ -447,6 +447,7 @@ export * from './util/ContentTypes';
|
|||||||
export * from './util/FetchUtil';
|
export * from './util/FetchUtil';
|
||||||
export * from './util/GuardedStream';
|
export * from './util/GuardedStream';
|
||||||
export * from './util/HeaderUtil';
|
export * from './util/HeaderUtil';
|
||||||
|
export * from './util/IterableUtil';
|
||||||
export * from './util/PathUtil';
|
export * from './util/PathUtil';
|
||||||
export * from './util/PromiseUtil';
|
export * from './util/PromiseUtil';
|
||||||
export * from './util/QuadUtil';
|
export * from './util/QuadUtil';
|
||||||
|
94
src/util/IterableUtil.ts
Normal file
94
src/util/IterableUtil.ts
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
// Utility functions for iterables that avoid array conversion
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new iterable with the results of calling a provided function on every element in the calling array.
|
||||||
|
* Similar to the {@link Array.prototype.map} function.
|
||||||
|
* See the documentation of the above function for more details.
|
||||||
|
*
|
||||||
|
* @param iterable - Iterable on which to call the map function.
|
||||||
|
* @param callbackFn - Function that is called for every element.
|
||||||
|
* @param thisArg - Value to use as `this` when executing `callbackFn`.
|
||||||
|
*/
|
||||||
|
export function* map<TIn, TOut>(iterable: Iterable<TIn>, callbackFn: (element: TIn, index: number) => TOut,
|
||||||
|
thisArg?: any): Iterable<TOut> {
|
||||||
|
const boundMapFn = callbackFn.bind(thisArg);
|
||||||
|
let count = 0;
|
||||||
|
for (const value of iterable) {
|
||||||
|
yield boundMapFn(value, count);
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new iterable with all elements that pass the test implemented by the provided function.
|
||||||
|
* Similar to the {@link Array.prototype.filter} function.
|
||||||
|
* See the documentation of the above function for more details.
|
||||||
|
*
|
||||||
|
* @param iterable - Iterable on which to call the map function.
|
||||||
|
* @param callbackFn - Function that is called to test every element.
|
||||||
|
* @param thisArg - Value to use as `this` when executing `callbackFn`.
|
||||||
|
*/
|
||||||
|
export function* filter<T>(iterable: Iterable<T>, callbackFn: (element: T, index: number) => boolean,
|
||||||
|
thisArg?: any): Iterable<T> {
|
||||||
|
const boundFilterFn = callbackFn.bind(thisArg);
|
||||||
|
let count = 0;
|
||||||
|
for (const value of iterable) {
|
||||||
|
if (boundFilterFn(value, count)) {
|
||||||
|
yield value;
|
||||||
|
}
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new iterable that is a concatenation of all the iterables in the input.
|
||||||
|
* @param iterables - An iterable of which the contents will be concatenated into a new iterable.
|
||||||
|
*/
|
||||||
|
export function* concat<T>(iterables: Iterable<Iterable<T>>): Iterable<T> {
|
||||||
|
for (const iterable of iterables) {
|
||||||
|
yield* iterable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Similar to the {@link Array.prototype.reduce} function, but for an iterable.
|
||||||
|
* See the documentation of the above function for more details.
|
||||||
|
* The first element will be used as the initial value.
|
||||||
|
*
|
||||||
|
* @param iterable - Iterable of which to reduce the elements.
|
||||||
|
* @param callbackFn - A reducer function.
|
||||||
|
*/
|
||||||
|
export function reduce<TIn>(iterable: Iterable<TIn>,
|
||||||
|
callbackFn: (previousValue: TIn, currentValue: TIn, currentIndex: number) => TIn): TIn;
|
||||||
|
/**
|
||||||
|
* Similar to the {@link Array.prototype.reduce} function, but for an iterable.
|
||||||
|
* See the documentation of the above function for more details.
|
||||||
|
*
|
||||||
|
* @param iterable - Iterable of which to reduce the elements.
|
||||||
|
* @param callbackFn - A reducer function.
|
||||||
|
* @param initialValue - The value to start from.
|
||||||
|
*/
|
||||||
|
export function reduce<TIn, TOut>(iterable: Iterable<TIn>,
|
||||||
|
callbackFn: (previousValue: TOut, currentValue: TIn, currentIndex: number) => TOut, initialValue: TOut): TOut;
|
||||||
|
export function reduce<TIn, TOut>(iterable: Iterable<TIn>,
|
||||||
|
callbackFn: (previousValue: TOut, currentValue: TIn, currentIndex: number) => TOut, initialValue?: TOut): TOut {
|
||||||
|
const iterator = iterable[Symbol.iterator]();
|
||||||
|
let count = 0;
|
||||||
|
if (!initialValue) {
|
||||||
|
const next = iterator.next();
|
||||||
|
if (next.done) {
|
||||||
|
throw new TypeError('Iterable is empty and no initial value was provided.');
|
||||||
|
}
|
||||||
|
// `initialValue` being undefined means the first signature was used where TIn === TOut
|
||||||
|
initialValue = next.value as unknown as TOut;
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
let previousValue = initialValue;
|
||||||
|
let next = iterator.next();
|
||||||
|
while (!next.done) {
|
||||||
|
previousValue = callbackFn(previousValue, next.value, count);
|
||||||
|
next = iterator.next();
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
return previousValue;
|
||||||
|
}
|
41
test/unit/util/IterableUtil.test.ts
Normal file
41
test/unit/util/IterableUtil.test.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import { concat, filter, map, reduce } from '../../../src/util/IterableUtil';
|
||||||
|
|
||||||
|
describe('IterableUtil', (): void => {
|
||||||
|
describe('#mapIterable', (): void => {
|
||||||
|
it('maps the values to a new iterable.', async(): Promise<void> => {
|
||||||
|
const input = [ 1, 2, 3 ];
|
||||||
|
expect([ ...map(input, (val): number => val + 3) ]).toEqual([ 4, 5, 6 ]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#filterIterable', (): void => {
|
||||||
|
it('filters the values of the iterable.', async(): Promise<void> => {
|
||||||
|
const input = [ 1, 2, 3 ];
|
||||||
|
expect([ ...filter(input, (val): boolean => val % 2 === 1) ]).toEqual([ 1, 3 ]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#concatIterables', (): void => {
|
||||||
|
it('concatenates all the iterables.', async(): Promise<void> => {
|
||||||
|
const input = [[ 1, 2, 3 ], [ 4, 5, 6 ], [ 7, 8, 9 ]];
|
||||||
|
expect([ ...concat(input) ]).toEqual([ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#reduceIterable', (): void => {
|
||||||
|
it('reduces the values in an iterable.', async(): Promise<void> => {
|
||||||
|
const input = [ 1, 2, 3 ];
|
||||||
|
expect(reduce(input, (acc, cur): number => acc + cur)).toBe(6);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can take a starting value.', async(): Promise<void> => {
|
||||||
|
const input = [ 1, 2, 3 ];
|
||||||
|
expect(reduce(input, (acc, cur): number => acc + cur, 4)).toBe(10);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws an error if the iterable is empty and there is no initial value.', async(): Promise<void> => {
|
||||||
|
const input: number[] = [];
|
||||||
|
expect((): number => reduce(input, (acc, cur): number => acc + cur)).toThrow(TypeError);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user