mirror of
https://gitlab.com/openstapps/openstapps.git
synced 2026-01-20 16:42:56 +00:00
feat: pipeline improvements
This commit is contained in:
@@ -15,12 +15,15 @@
|
||||
|
||||
/**
|
||||
* Chunk array into smaller arrays of a specified size.
|
||||
* @param array The array to chunk.
|
||||
* @param chunkSize The size of each chunk.
|
||||
* @template T
|
||||
* @param array {T[]} The array to chunk.
|
||||
* @param [chunkSize] {number} The size of each chunk.
|
||||
* @returns {T[][]}
|
||||
*/
|
||||
export function chunk<T>(array: T[], chunkSize = 1): T[][] {
|
||||
export function chunk(array, chunkSize = 1) {
|
||||
const arrayCopy = [...array];
|
||||
const out: T[][] = [];
|
||||
/** @type {T[][]} */
|
||||
const out = [];
|
||||
if (chunkSize <= 0) return out;
|
||||
while (arrayCopy.length > 0) out.push(arrayCopy.splice(0, chunkSize));
|
||||
return out;
|
||||
@@ -15,8 +15,13 @@
|
||||
|
||||
/**
|
||||
* Returns the difference between two arrays.
|
||||
* @template T
|
||||
* @param a {T[]}
|
||||
* @param b {T[]}
|
||||
* @param transform {(item: T) => unknown}
|
||||
* @returns {T[]}
|
||||
*/
|
||||
export function differenceBy<T>(a: T[], b: T[], transform: (item: T) => unknown) {
|
||||
export function differenceBy(a, b, transform) {
|
||||
const disallowed = new Set(b.map(transform));
|
||||
|
||||
return a.filter(item => !disallowed.has(transform(item)));
|
||||
@@ -17,14 +17,20 @@
|
||||
* Gets a value from a nested object.
|
||||
* The path must be key names separated by dots.
|
||||
* If the path doesn't exist, undefined is returned.
|
||||
*
|
||||
* @template {unknown} U
|
||||
* @param object {object}
|
||||
* @param path {string}
|
||||
* @returns {U}
|
||||
*/
|
||||
export function get<U = unknown>(object: object, path: string): U {
|
||||
return path.split('.').reduce(
|
||||
(accumulator, current) =>
|
||||
accumulator?.hasOwnProperty(current)
|
||||
? // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(accumulator as any)[current]
|
||||
: undefined,
|
||||
object,
|
||||
) as unknown as U;
|
||||
export function get(object, path) {
|
||||
return /** @type {U} */ (
|
||||
path
|
||||
.split('.')
|
||||
.reduce(
|
||||
(accumulator, current) =>
|
||||
accumulator?.hasOwnProperty(current) ? /** @type {any} */ (accumulator)[current] : undefined,
|
||||
object,
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -15,31 +15,42 @@
|
||||
|
||||
/**
|
||||
* Group an array by a function
|
||||
* @template T
|
||||
* @param collection {T[]}
|
||||
* @param group {(item: T) => string | undefined}
|
||||
* @returns {Record<string, T[]>}
|
||||
*/
|
||||
export function groupBy<T>(collection: T[], group: (item: T) => string | undefined): Record<string, T[]> {
|
||||
return collection.reduce((accumulator: Record<string, T[]>, item) => {
|
||||
export function groupBy(collection, group) {
|
||||
return collection.reduce((accumulator, item) => {
|
||||
const key = group(item) ?? '';
|
||||
accumulator[key] = accumulator[key] ?? [];
|
||||
accumulator[key].push(item);
|
||||
return accumulator;
|
||||
}, {});
|
||||
}, /** @type {Record<string, T[]>} */ ({}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Group an array by a function (returns a Map, whose keys keep order info of items entry)
|
||||
* @template T
|
||||
* @param collection {T[]}
|
||||
* @param group {(item: T) => string | undefined}
|
||||
* @returns {Map<string, T[]>}
|
||||
*/
|
||||
export function groupByStable<T>(collection: T[], group: (item: T) => string | undefined): Map<string, T[]> {
|
||||
return collection.reduce((accumulator: Map<string, T[]>, item) => {
|
||||
export function groupByStable(collection, group) {
|
||||
return collection.reduce((accumulator, item) => {
|
||||
const key = group(item) ?? '';
|
||||
accumulator.set(key, accumulator.get(key) ?? []);
|
||||
accumulator.get(key)?.push(item);
|
||||
return accumulator;
|
||||
}, new Map<string, T[]>());
|
||||
}, /** @type {Map<string, T[]>} */ new Map());
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @template {object} T
|
||||
* @param collection {T[]}
|
||||
* @param property {keyof T}
|
||||
* @returns {Record<string, T[]>}
|
||||
*/
|
||||
export function groupByProperty<T extends object>(collection: T[], property: keyof T): Record<string, T[]> {
|
||||
return groupBy(collection, item => item[property] as unknown as string);
|
||||
export function groupByProperty(collection, property) {
|
||||
return groupBy(collection, item => item[property]);
|
||||
}
|
||||
@@ -14,14 +14,17 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create an object composed of keys generated from the results of running
|
||||
* each element of collection thru iteratee. The corresponding value of
|
||||
* each key is the last element responsible for generating the key. The
|
||||
* iteratee is invoked with one argument: (value).
|
||||
* Create an object composed of keys generated from the results of running each element of collection through iteratee.
|
||||
* The corresponding value of each key is the last element responsible for generating the key.
|
||||
* The iteratee is invoked with one argument: (value).
|
||||
* @template T
|
||||
* @param collection {T[]}
|
||||
* @param key {(item: T) => string | number}
|
||||
* @returns {Record<string, T>}
|
||||
*/
|
||||
export function keyBy<T>(collection: T[], key: (item: T) => string | number): Record<string, T> {
|
||||
export function keyBy(collection, key) {
|
||||
return collection.reduce((accumulator, item) => {
|
||||
accumulator[key(item)] = item;
|
||||
return accumulator;
|
||||
}, {} as Record<string | number, T>);
|
||||
}, /** @type {Record<string, T>} */ ({}));
|
||||
}
|
||||
@@ -19,19 +19,19 @@
|
||||
* Note: JavaScript is not multithreaded.
|
||||
* This will not let you run tasks in parallel, use it only to limit how many network requests should be
|
||||
* running at once.
|
||||
* @param items the items to iterate through
|
||||
* @param task the task to be run
|
||||
* @param limit the maximum number of tasks that should be run asynchronously
|
||||
* @template T
|
||||
* @template U
|
||||
* @param items {T[]} the items to iterate through
|
||||
* @param task {(item: T, index: number) => Promise<U>} the task to be run
|
||||
* @param [limit] {number} the maximum number of tasks that should be run asynchronously
|
||||
* @returns {Promise<U[]>}
|
||||
*/
|
||||
export async function mapAsyncLimit<T, U = void>(
|
||||
items: T[],
|
||||
task: (item: T, index: number) => Promise<U>,
|
||||
limit = 5,
|
||||
): Promise<U[]> {
|
||||
export async function mapAsyncLimit(items, task, limit = 5) {
|
||||
return Promise.all(
|
||||
Array.from({length: limit}).map(async () => {
|
||||
let i = 0;
|
||||
const results: U[] = [];
|
||||
/** @type {U[]} */
|
||||
const results = [];
|
||||
for (let item = items.shift(); item !== undefined; item = items.shift()) {
|
||||
results.push(await task(item, i));
|
||||
i++;
|
||||
@@ -15,12 +15,14 @@
|
||||
|
||||
/**
|
||||
* Maps the values of an object to a new object
|
||||
* @template {object} T
|
||||
* @template U
|
||||
* @param object {T}
|
||||
* @param transform {(value: T[keyof T], key: keyof T) => U}
|
||||
* @returns {{[key in keyof T]: U}}
|
||||
*/
|
||||
export function mapValues<T extends object, U>(
|
||||
object: T,
|
||||
transform: (value: T[keyof T], key: keyof T) => U,
|
||||
): {[key in keyof T]: U} {
|
||||
const result = {} as {[key in keyof T]: U};
|
||||
export function mapValues(object, transform) {
|
||||
const result = /** @type {{[key in keyof T]: U}} */ ({});
|
||||
|
||||
for (const key in object) {
|
||||
if (object.hasOwnProperty(key)) {
|
||||
@@ -15,9 +15,13 @@
|
||||
|
||||
/**
|
||||
* Returns the minimum value of a collection.
|
||||
* @template T
|
||||
* @param array {T[]}
|
||||
* @param transform {(item: T) => number | undefined}
|
||||
* @returns {T}
|
||||
*/
|
||||
export function minBy<T>(array: T[], transform: (item: T) => number | undefined): T {
|
||||
export function minBy(array, transform) {
|
||||
const transforms = array.map(transform);
|
||||
const min = Math.min(...(transforms.filter(it => !!it) as number[]));
|
||||
return array.find((_, i) => transforms[i] === min) as T;
|
||||
const min = Math.min(.../** @type {number[]} */ (transforms.filter(it => !!it)));
|
||||
return /** @type {T} */ (array.find((_, i) => transforms[i] === min));
|
||||
}
|
||||
@@ -15,9 +15,14 @@
|
||||
|
||||
/**
|
||||
* Returns a new object without the specified keys.
|
||||
* @template {object} T
|
||||
* @template {keyof T} U
|
||||
* @param object {T}
|
||||
* @param keys {U[]}
|
||||
* @returns {Omit<T, U>}
|
||||
*/
|
||||
export function omit<T extends object, U extends keyof T>(object: T, ...keys: U[]): Omit<T, U> {
|
||||
export function omit(object, ...keys) {
|
||||
const out = {...object};
|
||||
for (const key of keys) delete out[key];
|
||||
return out as Exclude<T, U>;
|
||||
return out;
|
||||
}
|
||||
@@ -16,13 +16,14 @@
|
||||
/**
|
||||
* Partitions a list into two lists. One with the elements that satisfy a predicate,
|
||||
* and one with the elements that don't satisfy the predicate.
|
||||
* @template T
|
||||
* @param array {T[]}
|
||||
* @param transform {(item: T) => boolean}
|
||||
* @returns {[T[], T[]]}
|
||||
*/
|
||||
export function partition<T>(array: T[], transform: (item: T) => boolean): [T[], T[]] {
|
||||
return array.reduce<[T[], T[]]>(
|
||||
(accumulator, item) => {
|
||||
accumulator[transform(item) ? 0 : 1].push(item);
|
||||
return accumulator;
|
||||
},
|
||||
[[], []],
|
||||
);
|
||||
export function partition(array, transform) {
|
||||
return array.reduce((accumulator, item) => {
|
||||
accumulator[transform(item) ? 0 : 1].push(item);
|
||||
return accumulator;
|
||||
}, /** @type {[T[], T[]]} */ ([[], []]));
|
||||
}
|
||||
@@ -15,27 +15,34 @@
|
||||
|
||||
/**
|
||||
* Pick a set of properties from an object
|
||||
* @template {object} T
|
||||
* @template {keyof T} U
|
||||
* @param object {T}
|
||||
* @param keys {U[]}
|
||||
* @returns {Pick<T, U>}
|
||||
*/
|
||||
export function pick<T extends object, U extends keyof T>(object: T, keys: U[]): Pick<T, U> {
|
||||
export function pick(object, keys) {
|
||||
return keys.reduce((accumulator, key) => {
|
||||
if (object.hasOwnProperty(key)) {
|
||||
accumulator[key] = object[key];
|
||||
}
|
||||
return accumulator;
|
||||
}, {} as Pick<T, U>);
|
||||
}, /** @type {Pick<T, U>} */ ({}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Pick a set of properties from an object using a predicate function
|
||||
* @template {object} T
|
||||
* @template {keyof T} U
|
||||
* @param object {T}
|
||||
* @param predicate {(value: T[U], key: U) => boolean}
|
||||
* @returns {Pick<T, U>}
|
||||
*/
|
||||
export function pickBy<T extends object, U extends keyof T>(
|
||||
object: T,
|
||||
predicate: (value: T[U], key: U) => boolean,
|
||||
): Pick<T, U> {
|
||||
return (Object.keys(object) as U[]).reduce((accumulator, key) => {
|
||||
export function pickBy(object, predicate) {
|
||||
return /** @type {U[]} */ (Object.keys(object)).reduce((accumulator, key) => {
|
||||
if (predicate(object[key], key)) {
|
||||
accumulator[key] = object[key];
|
||||
}
|
||||
return accumulator;
|
||||
}, {} as Pick<T, U>);
|
||||
}, /** @type {Pick<T, U>} */ ({}));
|
||||
}
|
||||
@@ -15,8 +15,11 @@
|
||||
|
||||
/**
|
||||
* Shuffles an array
|
||||
* @template T
|
||||
* @param array {T[]}
|
||||
* @returns {T[]}
|
||||
*/
|
||||
export function shuffle<T>(array: T[]): T[] {
|
||||
export function shuffle(array) {
|
||||
const copy = [...array];
|
||||
const out = [];
|
||||
|
||||
@@ -15,8 +15,11 @@
|
||||
|
||||
/**
|
||||
* sort function for two strings
|
||||
* @param [a] {string}
|
||||
* @param [b] {string}
|
||||
* @returns {number}
|
||||
*/
|
||||
export function stringSort(a = '', b = ''): number {
|
||||
export function stringSort(a = '', b = '') {
|
||||
if (a < b) return -1;
|
||||
if (a > b) return 1;
|
||||
return 0;
|
||||
@@ -24,9 +27,12 @@ export function stringSort(a = '', b = ''): number {
|
||||
|
||||
/**
|
||||
* sort function for two strings that allows for a custom transform
|
||||
* @template T
|
||||
* @param map {(item: T) => string | undefined}
|
||||
* @returns {(a: T, b: T) => number}
|
||||
*/
|
||||
export function stringSortBy<T>(map: (item: T) => string | undefined): (a: T, b: T) => number {
|
||||
return (a: T, b: T): number => {
|
||||
export function stringSortBy(map) {
|
||||
return (a, b) => {
|
||||
const aValue = map(a) || '';
|
||||
const bValue = map(b) || '';
|
||||
if (aValue < bValue) return -1;
|
||||
@@ -15,17 +15,20 @@
|
||||
|
||||
/**
|
||||
* Sum an an array
|
||||
* @template {object} T
|
||||
* @param collection {T[]}
|
||||
* @param transform {(value: T) => number | undefined}
|
||||
* @returns {number}
|
||||
*/
|
||||
export function sumBy<T extends object>(
|
||||
collection: T[],
|
||||
transform: (value: T) => number | undefined,
|
||||
): number {
|
||||
export function sumBy(collection, transform) {
|
||||
return collection.reduce((accumulator, item) => accumulator + (transform(item) || 0), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sum an array of numbers
|
||||
* @param collection {Array<number | undefined>}
|
||||
* @returns {number}
|
||||
*/
|
||||
export function sum(collection: Array<number | undefined>): number {
|
||||
return collection.reduce<number>((accumulator, item) => accumulator + (item || 0), 0);
|
||||
export function sum(collection) {
|
||||
return /** @type {number[]} */ (collection).reduce((accumulator, item) => accumulator + (item ?? 0), 0);
|
||||
}
|
||||
@@ -13,17 +13,15 @@
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
export type Tree<T> = {
|
||||
[key: string]: Tree<T>;
|
||||
} & {
|
||||
_?: T[] | undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @template T
|
||||
* @param items {T[]}
|
||||
* @param transform {(item: T) => string[]}
|
||||
* @returns {import('./types.js').Tree<T>}
|
||||
*/
|
||||
export function treeGroupBy<T>(items: T[], transform: (item: T) => string[]): Tree<T> {
|
||||
const tree: Tree<T> = {};
|
||||
export function treeGroupBy(items, transform) {
|
||||
/** @type {import('./types.js').Tree<T>} */
|
||||
const tree = {};
|
||||
|
||||
for (const item of items) {
|
||||
let currentTree = tree;
|
||||
@@ -34,7 +32,7 @@ export function treeGroupBy<T>(items: T[], transform: (item: T) => string[]): Tr
|
||||
}
|
||||
// eslint-disable-next-line unicorn/no-array-for-each
|
||||
keys.forEach((key, i) => {
|
||||
currentTree = currentTree[key] = (currentTree[key] ?? {}) as Tree<T>;
|
||||
currentTree = currentTree[key] = currentTree[key] ?? {};
|
||||
if (i === keys.length - 1) {
|
||||
currentTree._ = currentTree._ ?? [];
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
7
packages/collection-utils/src/types.d.ts
vendored
Normal file
7
packages/collection-utils/src/types.d.ts
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
export * from './index.js';
|
||||
|
||||
export type Tree<T> = {
|
||||
[key: string]: Tree<T>;
|
||||
} & {
|
||||
_?: T[] | undefined;
|
||||
};
|
||||
@@ -15,12 +15,16 @@
|
||||
|
||||
/**
|
||||
* Filter out duplicates from an array.
|
||||
* @template T
|
||||
* @param array {T[]}
|
||||
* @param transform {(item: T) => string | number}
|
||||
* @returns {T[]}
|
||||
*/
|
||||
export function uniqBy<T>(array: T[], transform: (item: T) => string | number): T[] {
|
||||
export function uniqBy(array, transform) {
|
||||
return Object.values(
|
||||
array.reduce((accumulator, current) => {
|
||||
accumulator[transform(current)] = current;
|
||||
return accumulator;
|
||||
}, {} as Record<string | number, T>),
|
||||
}, /** @type {Record<string, T>} */ ({})),
|
||||
);
|
||||
}
|
||||
@@ -15,7 +15,12 @@
|
||||
|
||||
/**
|
||||
* Zip two arrays together.
|
||||
* @template T
|
||||
* @template U
|
||||
* @param a {T[]}
|
||||
* @param b {U[]}
|
||||
* @returns {[T, U][]}
|
||||
*/
|
||||
export function zip<T, U>(a: T[], b: U[]): [T, U][] {
|
||||
export function zip(a, b) {
|
||||
return a.map((_, i) => [a[i], b[i]]);
|
||||
}
|
||||
Reference in New Issue
Block a user