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/GuardedStream';
|
||||
export * from './util/HeaderUtil';
|
||||
export * from './util/IterableUtil';
|
||||
export * from './util/PathUtil';
|
||||
export * from './util/PromiseUtil';
|
||||
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