UNPKG

@voiceflow/common

Version:

Junk drawer of utility functions

166 lines (165 loc) 7.02 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.mergeByIdentifier = exports.toArray = exports.inferUnion = exports.filterAndGetLastRemovedValue = exports.filterOutNullish = exports.isNotNullish = exports.isNullish = exports.asyncForEach = exports.hasIdenticalMembers = exports.diff = exports.findUnion = exports.createMap = exports.createEntries = exports.separate = exports.reorder = exports.tail = exports.head = exports.toggleMembership = exports.append = exports.insertAll = exports.insert = exports.replace = exports.withoutValues = exports.withoutValue = exports.without = exports.unique = void 0; const unique = (items) => Array.from(new Set(items)); exports.unique = unique; const without = (items, index) => index < 0 ? items : [...items.slice(0, index), ...items.slice(index + 1)]; exports.without = without; const withoutValue = (items, value) => (0, exports.without)(items, items.indexOf(value)); exports.withoutValue = withoutValue; const withoutValues = (items, values) => items.filter((item) => !values.includes(item)); exports.withoutValues = withoutValues; const replace = (items, index, item) => index < 0 ? items : [...items.slice(0, index), item, ...items.slice(index + 1)]; exports.replace = replace; const insert = (items, index, item) => index < 0 ? [item, ...items] : [...items.slice(0, index), item, ...items.slice(index)]; exports.insert = insert; const insertAll = (items, index, additionalItems) => index < 0 ? [...additionalItems, ...items] : [...items.slice(0, index), ...additionalItems, ...items.slice(index)]; exports.insertAll = insertAll; const append = (items, item) => (items.includes(item) ? items : [...items, item]); exports.append = append; const toggleMembership = (items, item) => items.includes(item) ? (0, exports.withoutValue)(items, item) : [...items, item]; exports.toggleMembership = toggleMembership; const head = (items) => { const [first, ...rest] = items; return [first, rest]; }; exports.head = head; const tail = (items) => { const last = items[items.length - 1]; const rest = items.slice(0, -1); return [rest, last]; }; exports.tail = tail; const reorder = (items, fromIndex, toIndex) => { if (fromIndex < 0 || fromIndex >= items.length) { return items; } if (toIndex <= 0) { return [items[fromIndex], ...(0, exports.without)(items, fromIndex)]; } if (toIndex >= items.length) { return [...(0, exports.without)(items, fromIndex), items[fromIndex]]; } return (0, exports.insert)((0, exports.without)(items, fromIndex), toIndex, items[fromIndex]); }; exports.reorder = reorder; const separate = (items, predicate) => items.reduce(([passAcc, failAcc], item, index) => { if (predicate(item, index)) { passAcc.push(item); } else { failAcc.push(item); } return [passAcc, failAcc]; }, [[], []]); exports.separate = separate; const createEntries = (array, getKey = (value) => value) => array.map((item) => [getKey(item), item]); exports.createEntries = createEntries; const createMap = (array, getKey = (value) => value) => Object.fromEntries((0, exports.createEntries)(array, getKey)); exports.createMap = createMap; const findUnion = (lhs, rhs) => { // using sets instead of arrays since .has is O(1) const lSet = new Set(lhs); const rSet = new Set(rhs); const unionSet = new Set([...lhs, ...rhs]); const result = { rhsOnly: [], lhsOnly: [], union: [] }; for (const item of unionSet) { if (lSet.has(item)) { if (rSet.has(item)) { result.union.push(item); } else { result.lhsOnly.push(item); } } else { result.rhsOnly.push(item); } } return result; }; exports.findUnion = findUnion; const diff = (lhs, rhs) => { const { lhsOnly, rhsOnly } = (0, exports.findUnion)(lhs, rhs); return [...lhsOnly, ...rhsOnly]; }; exports.diff = diff; const hasIdenticalMembers = (lhs, rhs) => { if (lhs.length !== rhs.length) { return false; } if (!lhs.length && !rhs.length) { return true; } return !lhs.some((value) => !rhs.includes(value)); }; exports.hasIdenticalMembers = hasIdenticalMembers; const asyncForEach = async (array, callback) => { for (let index = 0; index < array.length; index++) { // eslint-disable-next-line callback-return,no-await-in-loop await callback(array[index], index, array); } }; exports.asyncForEach = asyncForEach; const isNullish = (value) => value === null || value === undefined; exports.isNullish = isNullish; const isNotNullish = (value) => value !== null && value !== undefined; exports.isNotNullish = isNotNullish; /** @deprecated Use `array.filter(isNotNullish)` instead. */ const filterOutNullish = (items) => items.filter(exports.isNotNullish); exports.filterOutNullish = filterOutNullish; // mostly just saves us needing to traverse an array twice const filterAndGetLastRemovedValue = (list, filter) => { let lastItem = null; const filteredList = list.filter((a) => { if (filter(a)) return true; lastItem = a; return false; }); return [filteredList, lastItem]; }; exports.filterAndGetLastRemovedValue = filterAndGetLastRemovedValue; const inferUnion = (array) => array; exports.inferUnion = inferUnion; const toArray = (valueOrArray) => (Array.isArray(valueOrArray) ? valueOrArray : [valueOrArray]); exports.toArray = toArray; /** * Merge together two arrays, if two items have the same identity based on the {@link identify} function * they will be merged together using the {@link merge} function provided. * @param items Array of items as a starting base. * @param newItems Array of items to merge in. * @param identify Function returning how to identify an item in the array * @param merge Function given two matching item identifiers, returning a single merged result * @example * const existingItems = [{a: 1, b: [1, 2, 3]}, {a: 2, b: [4]}]; * const newItems = [{a: 1, b: [5]}, {a: 3, b: [6, 7]}]; * * const items = mergeByIdentifier( * existingItems, * newItems, * (item) => item.a, * (existingItem, newItem) => { * return { * ...existingItem, * b: [...existingItem.b, ...newItem.b] * } * } * ); * * items == [{a: 1, b: [1, 2, 3, 5]}, {a: 2, b: [4]}, {a: 3, b: [6, 7]}]; */ const mergeByIdentifier = (items, newItems, identify, merge) => { const newItemsMap = new Map(newItems.map((newItem, i) => [identify(newItem, i), newItem])); const result = items.map((item, i) => { const itemIdentity = identify(item, i); const matchingNewItem = newItemsMap.get(itemIdentity); if (matchingNewItem) { newItemsMap.delete(itemIdentity); return merge(item, matchingNewItem); } return item; }); return result.concat(Array.from(newItemsMap.values())); }; exports.mergeByIdentifier = mergeByIdentifier;