UNPKG

svelte-ux

Version:

- Increment version in `package.json` and commit as `Version bump to x.y.z` - `npm run publish`

210 lines (209 loc) 6.78 kB
import { greatest } from 'd3-array'; import { propAccessor } from './object'; // Helper until Array.flat is more mainstream - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat // See also: https://lodash.com/docs/4.17.11#flatten // https://stackoverflow.com/a/55345130/191902 export function flatten(items) { return items.reduce((prev, next) => prev.concat(next), []); } /** * Combine values using reducer. Returns null if all values null (unlike d3.sum) */ export function combine(values, func) { if (values.every((x) => x == null)) { return null; } return values.reduce(func); } /** * Sum values but maintain null if all values null (unlike d3.sum) */ export function sum(items, prop) { const getProp = propAccessor(prop); const values = items.map((x) => getProp(x)); return combine(values, (total, operand) => (total || 0) + (operand || 0)); } /** * Subtract each value from previous but maintain null if all values null (unlike d3.sum) */ export function subtract(items, prop) { const getProp = propAccessor(prop); const values = items.map((x) => getProp(x)); return combine(values, (total, operand) => (total || 0) - (operand || 0)); } /** * Average values but maintain null if all values null (unlike d3.mean) */ export function average(items, prop) { const total = sum(items, prop); return total !== null ? total / items.length : null; } /** * Moving average. * @see https://observablehq.com/@d3/moving-average * @see https://mathworld.wolfram.com/MovingAverage.html */ export function movingAverage(items, windowSize, prop) { const getProp = propAccessor(prop); let sum = 0; const means = items.map((item, i) => { const value = getProp(item); sum += value !== null && value !== void 0 ? value : 0; if (i >= windowSize - 1) { const mean = sum / windowSize; // Remove oldest item in window for next iteration const oldestValue = getProp(items[i - windowSize + 1]); sum -= oldestValue !== null && oldestValue !== void 0 ? oldestValue : 0; return mean; } else { // Not enough values available in window yet return null; } }); return means; } /** * Return the unique set of values (remove duplicates) */ export function unique(values) { return Array.from(new Set(values)); } /** * Join values up to a maximum with `separator`, then truncate with total */ export function joinValues(values = [], max = 3, separator = ', ') { const total = values.length; if (total <= max) { return values.join(separator); } else { if (max === 0) { if (values.length === 1) { return values[0]; } else { return `(${total} total)`; } } else { return `${values.slice(0, max).join(separator)}, ... (${total} total)`; } } } /** * Recursively transverse nested arrays by path */ export function nestedFindByPath(arr, path, props, depth = 0) { var _a, _b; const getKeyProp = propAccessor((_a = props === null || props === void 0 ? void 0 : props.key) !== null && _a !== void 0 ? _a : 'key'); const getValuesProp = propAccessor((_b = props === null || props === void 0 ? void 0 : props.values) !== null && _b !== void 0 ? _b : 'values'); const item = arr.find((x) => getKeyProp(x) === path[depth]); if (depth === path.length - 1) { return item; } else { const children = getValuesProp(item); if (children) { return nestedFindByPath(getValuesProp(item), path, props, depth + 1); } } } /** * Recursively transverse nested arrays looking for item */ export function nestedFindByPredicate(arr, predicate, childrenProp) { const getChildrenProp = propAccessor(childrenProp !== null && childrenProp !== void 0 ? childrenProp : 'children'); let match = arr.find(predicate); if (match) { return match; } else { for (var item of arr) { const children = getChildrenProp(item); if (children) { match = nestedFindByPredicate(getChildrenProp(item), predicate, childrenProp); if (match) { return match; } } } } return undefined; } /** * Transverse array tree in depth-first order and execute callback for each item */ export function walk(arr, children, callback) { arr.forEach((item) => { callback(item); if (children(item)) { walk(children(item), children, callback); } }); } /** * Build flatten array in depth-first order (using `walk`) */ export function flattenTree(arr, children) { const flatArray = []; walk(arr, children, (item) => flatArray.push(item)); return flatArray; } export function chunk(array, size) { return array.reduce((acc, item, index) => { const bucket = Math.floor(index / size); if (!acc[bucket]) { acc[bucket] = []; } acc[bucket].push(item); return acc; }, []); } /** * Get evenly spaced samples from array * see: https://observablehq.com/@mbostock/evenly-spaced-sampling * see also: https://observablehq.com/@jonhelfman/uniform-sampling-variants */ export function samples(array, size) { if (!((size = Math.floor(size)) > 0)) return []; // return nothing const n = array.length; if (!(n > size)) return [...array]; // return everything if (size === 1) return [array[n >> 1]]; // return the midpoint return Array.from({ length: size }, (_, i) => array[Math.round((i / (size - 1)) * (n - 1))]); } /** * Adds item at `index` and returns array * Note: mutates, wrap with immer `produce(array, draft => addItem(draft))` for immutable */ export function addItem(array, item, index) { array.splice(index, 0, item); return array; } /** * Move item `from` index `to` index and returns array * Note: mutates, wrap with immer `produce(array, draft => moveItem(draft))` for immutable */ export function moveItem(array, from, to) { var item = array[from]; array.splice(from, 1); array.splice(to, 0, item); return array; } /** * Remove item at `index` returns array (not removed item) * Note: mutates, wrap with immer `produce(array, draft => removeItem(draft))` for immutable */ export function removeItem(array, index) { array.splice(index, 1); return array; } /** * Get the greatest absolute value in an array of numbers */ export function greatestAbs(array) { return greatest(array, (a, b) => Math.abs(a) - Math.abs(b)); }