@voiceflow/common
Version:
Junk drawer of utility functions
166 lines (165 loc) • 7.02 kB
JavaScript
;
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;