@tidyjs/tidy
Version:
Tidy up your data with JavaScript, inspired by dplyr and the tidyverse
1,408 lines (1,340 loc) • 46.8 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('d3-array')) :
typeof define === 'function' && define.amd ? define(['exports', 'd3-array'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.Tidy = {}, global.d3));
}(this, (function (exports, d3Array) { 'use strict';
function tidy(items, ...fns) {
if (typeof items === "function") {
throw new Error("You must supply the data as the first argument to tidy()");
}
let result = items;
for (const fn of fns) {
if (fn) {
result = fn(result);
}
}
return result;
}
function filter(filterFn) {
const _filter = (items) => items.filter(filterFn);
return _filter;
}
function when(predicate, fns) {
const _when = (items) => {
if (typeof predicate === "function") {
if (!predicate(items))
return items;
} else if (!predicate) {
return items;
}
const results = tidy(items, ...fns);
return results;
};
return _when;
}
function map(mapFn) {
const _map = (items) => items.map(mapFn);
return _map;
}
function singleOrArray(d) {
return d == null ? [] : Array.isArray(d) ? d : [d];
}
function distinct(keys) {
const _distinct = (items) => {
keys = singleOrArray(keys);
if (!keys.length) {
const set = new Set();
for (const item of items) {
set.add(item);
}
return Array.from(set);
}
const rootMap = new Map();
const distinctItems = [];
const lastKey = keys[keys.length - 1];
for (const item of items) {
let map = rootMap;
let hasItem = false;
for (const key of keys) {
const mapItemKey = typeof key === "function" ? key(item) : item[key];
if (key === lastKey) {
hasItem = map.has(mapItemKey);
if (!hasItem) {
distinctItems.push(item);
map.set(mapItemKey, true);
}
break;
}
if (!map.has(mapItemKey)) {
map.set(mapItemKey, new Map());
}
map = map.get(mapItemKey);
}
}
return distinctItems;
};
return _distinct;
}
function arrange(comparators) {
const _arrange = (items) => {
const comparatorFns = singleOrArray(comparators).map((comp) => typeof comp === "function" ? comp.length === 1 ? asc(comp) : comp : asc(comp));
return items.slice().sort((a, b) => {
for (const comparator of comparatorFns) {
const result = comparator(a, b);
if (result)
return result;
}
return 0;
});
};
return _arrange;
}
function asc(key) {
const keyFn = typeof key === "function" ? key : (d) => d[key];
return function _asc(a, b) {
return emptyAwareComparator(keyFn(a), keyFn(b), false);
};
}
function desc(key) {
const keyFn = typeof key === "function" ? key : (d) => d[key];
return function _desc(a, b) {
return emptyAwareComparator(keyFn(a), keyFn(b), true);
};
}
function fixedOrder(key, order, options) {
let {position = "start"} = options != null ? options : {};
const positionFactor = position === "end" ? -1 : 1;
const indexMap = new Map();
for (let i = 0; i < order.length; ++i) {
indexMap.set(order[i], i);
}
const keyFn = typeof key === "function" ? key : (d) => d[key];
return function _fixedOrder(a, b) {
var _a, _b;
const aIndex = (_a = indexMap.get(keyFn(a))) != null ? _a : -1;
const bIndex = (_b = indexMap.get(keyFn(b))) != null ? _b : -1;
if (aIndex >= 0 && bIndex >= 0) {
return aIndex - bIndex;
}
if (aIndex >= 0) {
return positionFactor * -1;
}
if (bIndex >= 0) {
return positionFactor * 1;
}
return 0;
};
}
function emptyAwareComparator(aInput, bInput, desc2) {
let a = desc2 ? bInput : aInput;
let b = desc2 ? aInput : bInput;
if (isEmpty(a) && isEmpty(b)) {
const rankA = a !== a ? 0 : a === null ? 1 : 2;
const rankB = b !== b ? 0 : b === null ? 1 : 2;
const order = rankA - rankB;
return desc2 ? -order : order;
}
if (isEmpty(a)) {
return desc2 ? -1 : 1;
}
if (isEmpty(b)) {
return desc2 ? 1 : -1;
}
return d3Array.ascending(a, b);
}
function isEmpty(value) {
return value == null || value !== value;
}
function summarize(summarizeSpec, options) {
const _summarize = (items) => {
options = options != null ? options : {};
const summarized = {};
const keys = Object.keys(summarizeSpec);
for (const key of keys) {
summarized[key] = summarizeSpec[key](items);
}
if (options.rest && items.length) {
const objectKeys = Object.keys(items[0]);
for (const objKey of objectKeys) {
if (keys.includes(objKey)) {
continue;
}
summarized[objKey] = options.rest(objKey)(items);
}
}
return [summarized];
};
return _summarize;
}
function _summarizeHelper(items, summaryFn, predicateFn, keys) {
if (!items.length)
return [];
const summarized = {};
let keysArr;
if (keys == null) {
keysArr = Object.keys(items[0]);
} else {
keysArr = [];
for (const keyInput of singleOrArray(keys)) {
if (typeof keyInput === "function") {
keysArr.push(...keyInput(items));
} else {
keysArr.push(keyInput);
}
}
}
for (const key of keysArr) {
if (predicateFn) {
const vector = items.map((d) => d[key]);
if (!predicateFn(vector)) {
continue;
}
}
summarized[key] = summaryFn(key)(items);
}
return [summarized];
}
function summarizeAll(summaryFn) {
const _summarizeAll = (items) => _summarizeHelper(items, summaryFn);
return _summarizeAll;
}
function summarizeIf(predicateFn, summaryFn) {
const _summarizeIf = (items) => _summarizeHelper(items, summaryFn, predicateFn);
return _summarizeIf;
}
function summarizeAt(keys, summaryFn) {
const _summarizeAt = (items) => _summarizeHelper(items, summaryFn, void 0, keys);
return _summarizeAt;
}
function mutate(mutateSpec) {
const _mutate = (items) => {
const mutatedItems = items.map((d) => ({...d}));
let i = 0;
for (const mutatedItem of mutatedItems) {
for (const key in mutateSpec) {
const mutateSpecValue = mutateSpec[key];
const mutatedResult = typeof mutateSpecValue === "function" ? mutateSpecValue(mutatedItem, i, mutatedItems) : mutateSpecValue;
mutatedItem[key] = mutatedResult;
}
++i;
}
return mutatedItems;
};
return _mutate;
}
function total(summarizeSpec, mutateSpec) {
const _total = (items) => {
const summarized = summarize(summarizeSpec)(items);
const mutated = mutate(mutateSpec)(summarized);
return [...items, ...mutated];
};
return _total;
}
function totalAll(summaryFn, mutateSpec) {
const _totalAll = (items) => {
const summarized = summarizeAll(summaryFn)(items);
const mutated = mutate(mutateSpec)(summarized);
return [...items, ...mutated];
};
return _totalAll;
}
function totalIf(predicateFn, summaryFn, mutateSpec) {
const _totalIf = (items) => {
const summarized = summarizeIf(predicateFn, summaryFn)(items);
const mutated = mutate(mutateSpec)(summarized);
return [...items, ...mutated];
};
return _totalIf;
}
function totalAt(keys, summaryFn, mutateSpec) {
const _totalAt = (items) => {
const summarized = summarizeAt(keys, summaryFn)(items);
const mutated = mutate(mutateSpec)(summarized);
return [...items, ...mutated];
};
return _totalAt;
}
function assignGroupKeys(d, keys) {
if (d == null || typeof d !== "object" || Array.isArray(d))
return d;
const keysObj = Object.fromEntries(keys.filter((key) => typeof key[0] !== "function" && key[0] != null));
return Object.assign(keysObj, d);
}
function groupTraversal(grouped, outputGrouped, keys, addSubgroup, addLeaves, level = 0) {
for (const [key, value] of grouped.entries()) {
const keysHere = [...keys, key];
if (value instanceof Map) {
const subgroup = addSubgroup(outputGrouped, keysHere, level);
groupTraversal(value, subgroup, keysHere, addSubgroup, addLeaves, level + 1);
} else {
addLeaves(outputGrouped, keysHere, value, level);
}
}
return outputGrouped;
}
function groupMap(grouped, groupFn, keyFn = (keys) => keys[keys.length - 1]) {
function addSubgroup(parentGrouped, keys) {
const subgroup = new Map();
parentGrouped.set(keyFn(keys), subgroup);
return subgroup;
}
function addLeaves(parentGrouped, keys, values) {
parentGrouped.set(keyFn(keys), groupFn(values, keys));
}
const outputGrouped = new Map();
groupTraversal(grouped, outputGrouped, [], addSubgroup, addLeaves);
return outputGrouped;
}
const identity = (d) => d;
function isObject(obj) {
const type = typeof obj;
return obj != null && (type === "object" || type === "function");
}
function groupBy(groupKeys, fns, options) {
if (typeof fns === "function") {
fns = [fns];
} else if (arguments.length === 2 && fns != null && !Array.isArray(fns)) {
options = fns;
}
const _groupBy = (items) => {
const grouped = makeGrouped(items, groupKeys);
const results = runFlow(grouped, fns, options == null ? void 0 : options.addGroupKeys);
if (options == null ? void 0 : options.export) {
switch (options.export) {
case "grouped":
return results;
case "levels":
return exportLevels(results, options);
case "entries-obj":
case "entriesObject":
return exportLevels(results, {
...options,
export: "levels",
levels: ["entries-object"]
});
default:
return exportLevels(results, {
...options,
export: "levels",
levels: [options.export]
});
}
}
const ungrouped = ungroup(results, options == null ? void 0 : options.addGroupKeys);
return ungrouped;
};
return _groupBy;
}
groupBy.grouped = (options) => ({...options, export: "grouped"});
groupBy.entries = (options) => ({...options, export: "entries"});
groupBy.entriesObject = (options) => ({...options, export: "entries-object"});
groupBy.object = (options) => ({...options, export: "object"});
groupBy.map = (options) => ({...options, export: "map"});
groupBy.keys = (options) => ({...options, export: "keys"});
groupBy.values = (options) => ({...options, export: "values"});
groupBy.levels = (options) => ({...options, export: "levels"});
function runFlow(items, fns, addGroupKeys) {
let result = items;
if (!(fns == null ? void 0 : fns.length))
return result;
for (const fn of fns) {
if (!fn)
continue;
result = groupMap(result, (items2, keys) => {
const context = {groupKeys: keys};
let leafItemsMapped = fn(items2, context);
if (addGroupKeys !== false) {
leafItemsMapped = leafItemsMapped.map((item) => assignGroupKeys(item, keys));
}
return leafItemsMapped;
});
}
return result;
}
function makeGrouped(items, groupKeys) {
const groupKeyFns = singleOrArray(groupKeys).map((key, i) => {
const keyFn = typeof key === "function" ? key : (d) => d[key];
const keyCache = new Map();
return (d) => {
const keyValue = keyFn(d);
const keyValueOf = isObject(keyValue) ? keyValue.valueOf() : keyValue;
if (keyCache.has(keyValueOf)) {
return keyCache.get(keyValueOf);
}
const keyWithName = [key, keyValue];
keyCache.set(keyValueOf, keyWithName);
return keyWithName;
};
});
const grouped = d3Array.group(items, ...groupKeyFns);
return grouped;
}
function ungroup(grouped, addGroupKeys) {
const items = [];
groupTraversal(grouped, items, [], identity, (root, keys, values) => {
let valuesToAdd = values;
if (addGroupKeys !== false) {
valuesToAdd = values.map((d) => assignGroupKeys(d, keys));
}
root.push(...valuesToAdd);
});
return items;
}
const defaultCompositeKey = (keys) => keys.join("/");
function processFromGroupsOptions(options) {
var _a;
const {
flat,
single,
mapLeaf = identity,
mapLeaves = identity,
addGroupKeys
} = options;
let compositeKey;
if (options.flat) {
compositeKey = (_a = options.compositeKey) != null ? _a : defaultCompositeKey;
}
const groupFn = (values, keys) => {
return single ? mapLeaf(addGroupKeys === false ? values[0] : assignGroupKeys(values[0], keys)) : mapLeaves(values.map((d) => mapLeaf(addGroupKeys === false ? d : assignGroupKeys(d, keys))));
};
const keyFn = flat ? (keys) => compositeKey(keys.map((d) => d[1])) : (keys) => keys[keys.length - 1][1];
return {groupFn, keyFn};
}
function exportLevels(grouped, options) {
const {groupFn, keyFn} = processFromGroupsOptions(options);
let {mapEntry = identity} = options;
const {levels = ["entries"]} = options;
const levelSpecs = [];
for (const levelOption of levels) {
switch (levelOption) {
case "entries":
case "entries-object":
case "entries-obj":
case "entriesObject": {
const levelMapEntry = (levelOption === "entries-object" || levelOption === "entries-obj" || levelOption === "entriesObject") && options.mapEntry == null ? ([key, values]) => ({key, values}) : mapEntry;
levelSpecs.push({
id: "entries",
createEmptySubgroup: () => [],
addSubgroup: (parentGrouped, newSubgroup, key, level) => {
parentGrouped.push(levelMapEntry([key, newSubgroup], level));
},
addLeaf: (parentGrouped, key, values, level) => {
parentGrouped.push(levelMapEntry([key, values], level));
}
});
break;
}
case "map":
levelSpecs.push({
id: "map",
createEmptySubgroup: () => new Map(),
addSubgroup: (parentGrouped, newSubgroup, key) => {
parentGrouped.set(key, newSubgroup);
},
addLeaf: (parentGrouped, key, values) => {
parentGrouped.set(key, values);
}
});
break;
case "object":
levelSpecs.push({
id: "object",
createEmptySubgroup: () => ({}),
addSubgroup: (parentGrouped, newSubgroup, key) => {
parentGrouped[key] = newSubgroup;
},
addLeaf: (parentGrouped, key, values) => {
parentGrouped[key] = values;
}
});
break;
case "keys":
levelSpecs.push({
id: "keys",
createEmptySubgroup: () => [],
addSubgroup: (parentGrouped, newSubgroup, key) => {
parentGrouped.push([key, newSubgroup]);
},
addLeaf: (parentGrouped, key) => {
parentGrouped.push(key);
}
});
break;
case "values":
levelSpecs.push({
id: "values",
createEmptySubgroup: () => [],
addSubgroup: (parentGrouped, newSubgroup) => {
parentGrouped.push(newSubgroup);
},
addLeaf: (parentGrouped, key, values) => {
parentGrouped.push(values);
}
});
break;
default: {
if (typeof levelOption === "object") {
levelSpecs.push(levelOption);
}
}
}
}
const addSubgroup = (parentGrouped, keys, level) => {
var _a, _b;
if (options.flat) {
return parentGrouped;
}
const levelSpec = (_a = levelSpecs[level]) != null ? _a : levelSpecs[levelSpecs.length - 1];
const nextLevelSpec = (_b = levelSpecs[level + 1]) != null ? _b : levelSpec;
const newSubgroup = nextLevelSpec.createEmptySubgroup();
levelSpec.addSubgroup(parentGrouped, newSubgroup, keyFn(keys), level);
return newSubgroup;
};
const addLeaf = (parentGrouped, keys, values, level) => {
var _a;
const levelSpec = (_a = levelSpecs[level]) != null ? _a : levelSpecs[levelSpecs.length - 1];
levelSpec.addLeaf(parentGrouped, keyFn(keys), groupFn(values, keys), level);
};
const initialOutputObject = levelSpecs[0].createEmptySubgroup();
return groupTraversal(grouped, initialOutputObject, [], addSubgroup, addLeaf);
}
function n(options) {
if (options == null ? void 0 : options.predicate) {
const predicate = options.predicate;
return (items) => items.reduce((n2, d, i) => predicate(d, i, items) ? n2 + 1 : n2, 0);
}
return (items) => items.length;
}
function sum(key, options) {
let keyFn = typeof key === "function" ? key : (d) => d[key];
if (options == null ? void 0 : options.predicate) {
const originalKeyFn = keyFn;
const predicate = options.predicate;
keyFn = (d, index, array) => predicate(d, index, array) ? originalKeyFn(d, index, array) : 0;
}
return (items) => d3Array.fsum(items, keyFn);
}
function tally(options) {
const _tally = (items) => {
const {name = "n", wt} = options != null ? options : {};
const summarized = summarize({[name]: wt == null ? n() : sum(wt)})(items);
return summarized;
};
return _tally;
}
function count(groupKeys, options) {
const _count = (items) => {
options = options != null ? options : {};
const {name = "n", sort} = options;
const results = tidy(items, groupBy(groupKeys, [tally(options)]), sort ? arrange(desc(name)) : identity);
return results;
};
return _count;
}
function rename(renameSpec) {
const _rename = (items) => {
return items.map((d) => {
var _a;
const mapped = {};
const keys = Object.keys(d);
for (const key of keys) {
const newKey = (_a = renameSpec[key]) != null ? _a : key;
mapped[newKey] = d[key];
}
return mapped;
});
};
return _rename;
}
function slice(start, end) {
const _slice = (items) => items.slice(start, end);
return _slice;
}
const sliceHead = (n) => slice(0, n);
const sliceTail = (n) => slice(-n);
function sliceMin(n, orderBy) {
const _sliceMin = (items) => arrange(orderBy)(items).slice(0, n);
return _sliceMin;
}
function sliceMax(n, orderBy) {
const _sliceMax = (items) => typeof orderBy === "function" ? arrange(orderBy)(items).slice(-n).reverse() : arrange(desc(orderBy))(items).slice(0, n);
return _sliceMax;
}
function sliceSample(n, options) {
options = options != null ? options : {};
const {replace} = options;
const _sliceSample = (items) => {
if (!items.length)
return items.slice();
if (replace) {
const sliced = [];
for (let i = 0; i < n; ++i) {
sliced.push(items[Math.floor(Math.random() * items.length)]);
}
return sliced;
}
return d3Array.shuffle(items.slice()).slice(0, n);
};
return _sliceSample;
}
function autodetectByMap(itemsA, itemsB) {
if (itemsA.length === 0 || itemsB.length === 0)
return {};
const keysA = Object.keys(itemsA[0]);
const keysB = Object.keys(itemsB[0]);
const byMap = {};
for (const key of keysA) {
if (keysB.includes(key)) {
byMap[key] = key;
}
}
return byMap;
}
function makeByMap(by) {
if (Array.isArray(by)) {
const byMap = {};
for (const key of by) {
byMap[key] = key;
}
return byMap;
} else if (typeof by === "object") {
return by;
}
return {[by]: by};
}
function isMatch(d, j, byMap) {
for (const jKey in byMap) {
const dKey = byMap[jKey];
if (d[dKey] !== j[jKey]) {
return false;
}
}
return true;
}
function innerJoin(itemsToJoin, options) {
const _innerJoin = (items) => {
const byMap = (options == null ? void 0 : options.by) == null ? autodetectByMap(items, itemsToJoin) : makeByMap(options.by);
const joined = items.flatMap((d) => {
const matches = itemsToJoin.filter((j) => isMatch(d, j, byMap));
return matches.map((j) => ({...d, ...j}));
});
return joined;
};
return _innerJoin;
}
function leftJoin(itemsToJoin, options) {
const _leftJoin = (items) => {
if (!itemsToJoin.length)
return items;
const byMap = (options == null ? void 0 : options.by) == null ? autodetectByMap(items, itemsToJoin) : makeByMap(options.by);
const joinObjectKeys = Object.keys(itemsToJoin[0]);
const joined = items.flatMap((d) => {
const matches = itemsToJoin.filter((j) => isMatch(d, j, byMap));
if (matches.length) {
return matches.map((j) => ({...d, ...j}));
}
const undefinedFill = Object.fromEntries(joinObjectKeys.filter((key) => d[key] == null).map((key) => [key, void 0]));
return {...d, ...undefinedFill};
});
return joined;
};
return _leftJoin;
}
function fullJoin(itemsToJoin, options) {
const _fullJoin = (items) => {
if (!itemsToJoin.length)
return items;
if (!items.length)
return itemsToJoin;
const byMap = (options == null ? void 0 : options.by) == null ? autodetectByMap(items, itemsToJoin) : makeByMap(options.by);
const matchMap = new Map();
const joinObjectKeys = Object.keys(itemsToJoin[0]);
const joined = items.flatMap((d) => {
const matches = itemsToJoin.filter((j) => {
const matched = isMatch(d, j, byMap);
if (matched) {
matchMap.set(j, true);
}
return matched;
});
if (matches.length) {
return matches.map((j) => ({...d, ...j}));
}
const undefinedFill = Object.fromEntries(joinObjectKeys.filter((key) => d[key] == null).map((key) => [key, void 0]));
return {...d, ...undefinedFill};
});
if (matchMap.size < itemsToJoin.length) {
const leftEmptyObject = Object.fromEntries(Object.keys(items[0]).map((key) => [key, void 0]));
for (const item of itemsToJoin) {
if (!matchMap.has(item)) {
joined.push({...leftEmptyObject, ...item});
}
}
}
return joined;
};
return _fullJoin;
}
function mutateWithSummary(mutateSpec) {
const _mutate = (items) => {
const mutatedItems = items.map((d) => ({...d}));
for (const key in mutateSpec) {
const mutateSpecValue = mutateSpec[key];
const mutatedResult = typeof mutateSpecValue === "function" ? mutateSpecValue(mutatedItems) : mutateSpecValue;
const mutatedVector = (mutatedResult == null ? void 0 : mutatedResult[Symbol.iterator]) && typeof mutatedResult !== "string" ? mutatedResult : items.map(() => mutatedResult);
let i = -1;
for (const mutatedItem of mutatedItems) {
mutatedItem[key] = mutatedVector[++i];
}
}
return mutatedItems;
};
return _mutate;
}
function keysFromItems(items) {
if (items.length < 1)
return [];
const keys = Object.keys(items[0]);
return keys;
}
function everything() {
return (items) => {
const keys = keysFromItems(items);
return keys;
};
}
function processSelectors(items, selectKeys) {
let processedSelectKeys = [];
for (const keyInput of singleOrArray(selectKeys)) {
if (typeof keyInput === "function") {
processedSelectKeys.push(...keyInput(items));
} else {
processedSelectKeys.push(keyInput);
}
}
if (processedSelectKeys.length && processedSelectKeys[0][0] === "-") {
processedSelectKeys = [...everything()(items), ...processedSelectKeys];
}
const negationMap = {};
const keysWithoutNegations = [];
for (let k = processedSelectKeys.length - 1; k >= 0; k--) {
const key = processedSelectKeys[k];
if (key[0] === "-") {
negationMap[key.substring(1)] = true;
continue;
}
if (negationMap[key]) {
negationMap[key] = false;
continue;
}
keysWithoutNegations.unshift(key);
}
processedSelectKeys = Array.from(new Set(keysWithoutNegations));
return processedSelectKeys;
}
function select(selectKeys) {
const _select = (items) => {
let processedSelectKeys = processSelectors(items, selectKeys);
if (!processedSelectKeys.length)
return items;
return items.map((d) => {
const mapped = {};
for (const key of processedSelectKeys) {
mapped[key] = d[key];
}
return mapped;
});
};
return _select;
}
function transmute(mutateSpec) {
const _transmute = (items) => {
const mutated = mutate(mutateSpec)(items);
const picked = select(Object.keys(mutateSpec))(mutated);
return picked;
};
return _transmute;
}
function addRows(itemsToAdd) {
const _addRows = (items) => {
if (typeof itemsToAdd === "function") {
return [...items, ...singleOrArray(itemsToAdd(items))];
}
return [...items, ...singleOrArray(itemsToAdd)];
};
return _addRows;
}
function pivotWider(options) {
const _pivotWider = (items) => {
const {
namesFrom,
valuesFrom,
valuesFill,
valuesFillMap,
namesSep = "_"
} = options;
const namesFromKeys = Array.isArray(namesFrom) ? namesFrom : [namesFrom];
const valuesFromKeys = Array.isArray(valuesFrom) ? valuesFrom : [valuesFrom];
const wider = [];
if (!items.length)
return wider;
const idColumns = Object.keys(items[0]).filter((key) => !namesFromKeys.includes(key) && !valuesFromKeys.includes(key));
const nameValuesMap = {};
for (const item of items) {
for (const nameKey of namesFromKeys) {
if (nameValuesMap[nameKey] == null) {
nameValuesMap[nameKey] = {};
}
nameValuesMap[nameKey][item[nameKey]] = true;
}
}
const nameValuesLists = [];
for (const nameKey in nameValuesMap) {
nameValuesLists.push(Object.keys(nameValuesMap[nameKey]));
}
const baseWideObj = {};
const combos = makeCombinations(namesSep, nameValuesLists);
for (const nameKey of combos) {
if (valuesFromKeys.length === 1) {
baseWideObj[nameKey] = valuesFillMap != null ? valuesFillMap[valuesFromKeys[0]] : valuesFill;
continue;
}
for (const valueKey of valuesFromKeys) {
baseWideObj[`${valueKey}${namesSep}${nameKey}`] = valuesFillMap != null ? valuesFillMap[valueKey] : valuesFill;
}
}
function widenItems(items2) {
if (!items2.length)
return [];
const wide = {...baseWideObj};
for (const idKey of idColumns) {
wide[idKey] = items2[0][idKey];
}
for (const item of items2) {
const nameKey = namesFromKeys.map((key) => item[key]).join(namesSep);
if (valuesFromKeys.length === 1) {
wide[nameKey] = item[valuesFromKeys[0]];
continue;
}
for (const valueKey of valuesFromKeys) {
wide[`${valueKey}${namesSep}${nameKey}`] = item[valueKey];
}
}
return [wide];
}
if (!idColumns.length) {
return widenItems(items);
}
const finish = tidy(items, groupBy(idColumns, [widenItems]));
return finish;
};
return _pivotWider;
}
function makeCombinations(separator = "_", arrays) {
function combine(accum, prefix, remainingArrays) {
if (!remainingArrays.length && prefix != null) {
accum.push(prefix);
return;
}
const array = remainingArrays[0];
const newRemainingArrays = remainingArrays.slice(1);
for (const item of array) {
combine(accum, prefix == null ? item : `${prefix}${separator}${item}`, newRemainingArrays);
}
}
const result = [];
combine(result, null, arrays);
return result;
}
function pivotLonger(options) {
const _pivotLonger = (items) => {
var _a;
const {namesTo, valuesTo, namesSep = "_"} = options;
const cols = (_a = options.cols) != null ? _a : [];
const colsKeys = processSelectors(items, cols);
const namesToKeys = Array.isArray(namesTo) ? namesTo : [namesTo];
const valuesToKeys = Array.isArray(valuesTo) ? valuesTo : [valuesTo];
const hasMultipleNamesTo = namesToKeys.length > 1;
const hasMultipleValuesTo = valuesToKeys.length > 1;
const longer = [];
for (const item of items) {
const remainingKeys = Object.keys(item).filter((key) => !colsKeys.includes(key));
const baseObj = {};
for (const key of remainingKeys) {
baseObj[key] = item[key];
}
const nameValueKeysWithoutValuePrefix = hasMultipleValuesTo ? Array.from(new Set(colsKeys.map((key) => key.substring(key.indexOf(namesSep) + 1)))) : colsKeys;
for (const nameValue of nameValueKeysWithoutValuePrefix) {
const entryObj = {...baseObj};
for (const valueKey of valuesToKeys) {
const itemKey = hasMultipleValuesTo ? `${valueKey}${namesSep}${nameValue}` : nameValue;
const nameValueParts = hasMultipleNamesTo ? nameValue.split(namesSep) : [nameValue];
let i = 0;
for (const nameKey of namesToKeys) {
const nameValuePart = nameValueParts[i++];
entryObj[nameKey] = nameValuePart;
entryObj[valueKey] = item[itemKey];
}
}
longer.push(entryObj);
}
}
return longer;
};
return _pivotLonger;
}
function expand(expandKeys) {
const _expand = (items) => {
const keyMap = makeKeyMap(expandKeys);
const vectors = [];
for (const key in keyMap) {
const keyValue = keyMap[key];
let values;
if (typeof keyValue === "function") {
values = keyValue(items);
} else if (Array.isArray(keyValue)) {
values = keyValue;
} else {
values = Array.from(new Set(items.map((d) => d[key])));
}
vectors.push(values.map((value) => ({[key]: value})));
}
return makeCombinations$1(vectors);
};
return _expand;
}
function makeCombinations$1(vectors) {
function combine(accum, baseObj, remainingVectors) {
if (!remainingVectors.length && baseObj != null) {
accum.push(baseObj);
return;
}
const vector = remainingVectors[0];
const newRemainingArrays = remainingVectors.slice(1);
for (const item of vector) {
combine(accum, {...baseObj, ...item}, newRemainingArrays);
}
}
const result = [];
combine(result, null, vectors);
return result;
}
function makeKeyMap(keys) {
if (Array.isArray(keys)) {
const keyMap = {};
for (const key of keys) {
keyMap[key] = key;
}
return keyMap;
} else if (typeof keys === "object") {
return keys;
}
return {[keys]: keys};
}
function vectorSeq(values, period = 1) {
let [min, max] = d3Array.extent(values);
const sequence = [];
let value = min;
while (value <= max) {
sequence.push(value);
value += period;
}
return sequence;
}
function vectorSeqDate(values, granularity = "day", period = 1) {
let [min, max] = d3Array.extent(values);
const sequence = [];
let value = new Date(min);
while (value <= max) {
sequence.push(new Date(value));
if (granularity === "second" || granularity === "s" || granularity === "seconds") {
value.setUTCSeconds(value.getUTCSeconds() + 1 * period);
} else if (granularity === "minute" || granularity === "min" || granularity === "minutes") {
value.setUTCMinutes(value.getUTCMinutes() + 1 * period);
} else if (granularity === "day" || granularity === "d" || granularity === "days") {
value.setUTCDate(value.getUTCDate() + 1 * period);
} else if (granularity === "week" || granularity === "w" || granularity === "weeks") {
value.setUTCDate(value.getUTCDate() + 7 * period);
} else if (granularity === "month" || granularity === "m" || granularity === "months") {
value.setUTCMonth(value.getUTCMonth() + 1 * period);
} else if (granularity === "year" || granularity === "y" || granularity === "years") {
value.setUTCFullYear(value.getUTCFullYear() + 1 * period);
} else {
throw new Error("Invalid granularity for date sequence: " + granularity);
}
}
return sequence;
}
function fullSeq(key, period) {
return function fullSeqInner(items) {
period = period != null ? period : 1;
const keyFn = typeof key === "function" ? key : (d) => d[key];
return vectorSeq(items.map(keyFn), period);
};
}
function fullSeqDate(key, granularity, period) {
return function fullSeqDateInner(items) {
granularity = granularity != null ? granularity : "day";
period = period != null ? period : 1;
const keyFn = typeof key === "function" ? key : (d) => d[key];
return vectorSeqDate(items.map(keyFn), granularity, period);
};
}
function fullSeqDateISOString(key, granularity, period) {
return function fullSeqDateISOStringInner(items) {
granularity = granularity != null ? granularity : "day";
period = period != null ? period : 1;
const keyFn = typeof key === "function" ? key : (d) => d[key];
return vectorSeqDate(items.map((d) => new Date(keyFn(d))), granularity, period).map((date) => date.toISOString());
};
}
function replaceNully(replaceSpec) {
const _replaceNully = (items) => {
const replacedItems = [];
for (const d of items) {
const obj = {...d};
for (const key in replaceSpec) {
if (obj[key] == null) {
obj[key] = replaceSpec[key];
}
}
replacedItems.push(obj);
}
return replacedItems;
};
return _replaceNully;
}
function complete(expandKeys, replaceNullySpec) {
const _complete = (items) => {
const expanded = expand(expandKeys)(items);
const joined = leftJoin(items)(expanded);
return replaceNullySpec ? replaceNully(replaceNullySpec)(joined) : joined;
};
return _complete;
}
function fill(keys) {
const _fill = (items) => {
const keysArray = singleOrArray(keys);
const replaceMap = {};
return items.map((d) => {
const obj = {...d};
for (const key of keysArray) {
if (obj[key] != null) {
replaceMap[key] = obj[key];
} else if (replaceMap[key] != null) {
obj[key] = replaceMap[key];
}
}
return obj;
});
};
return _fill;
}
function debug(label, options) {
const _debug = (items, context) => {
var _a;
let prefix = "[tidy.debug";
if ((_a = context == null ? void 0 : context.groupKeys) == null ? void 0 : _a.length) {
const groupKeys = context.groupKeys;
const groupKeyStrings = groupKeys.map((keyPair) => keyPair.join(": ")).join(", ");
if (groupKeyStrings.length) {
prefix += "|" + groupKeyStrings;
}
}
options = options != null ? options : {};
const {limit = 10, output = "table"} = options;
const dashString = "--------------------------------------------------------------------------------";
let numDashes = dashString.length;
const prefixedLabel = prefix + "]" + (label == null ? "" : " " + label);
numDashes = Math.max(0, numDashes - (prefixedLabel.length + 2));
console.log(`${prefixedLabel} ${dashString.substring(0, numDashes)}`);
console[output](limit == null || limit >= items.length ? items : items.slice(0, limit));
return items;
};
return _debug;
}
function rate(numerator, denominator, allowDivideByZero) {
return numerator == null || denominator == null ? void 0 : denominator === 0 && numerator === 0 ? 0 : !allowDivideByZero && denominator === 0 ? void 0 : numerator / denominator;
}
function subtract(a, b, nullyZero) {
return a == null || b == null ? nullyZero ? (a != null ? a : 0) - (b != null ? b : 0) : void 0 : a - b;
}
function add(a, b, nullyZero) {
return a == null || b == null ? nullyZero ? (a != null ? a : 0) + (b != null ? b : 0) : void 0 : a + b;
}
var math = /*#__PURE__*/Object.freeze({
__proto__: null,
rate: rate,
subtract: subtract,
add: add
});
function rate$1(numerator, denominator, options) {
const numeratorFn = typeof numerator === "function" ? numerator : (d) => d[numerator];
const denominatorFn = typeof denominator === "function" ? denominator : (d) => d[denominator];
const {predicate, allowDivideByZero} = options != null ? options : {};
return predicate == null ? (d, index, array) => {
const denom = denominatorFn(d, index, array);
const numer = numeratorFn(d, index, array);
return rate(numer, denom, allowDivideByZero);
} : (d, index, array) => {
if (!predicate(d, index, array))
return void 0;
const denom = denominatorFn(d, index, array);
const numer = numeratorFn(d, index, array);
return rate(numer, denom, allowDivideByZero);
};
}
function fcumsum(items, accessor) {
let sum = new d3Array.Adder(), i = 0;
return Float64Array.from(items, (value) => sum.add(+(accessor(value, i++, items) || 0)));
}
function mean(items, accessor) {
let n = 0;
for (let i = 0; i < items.length; ++i) {
const value = accessor(items[i], i, items);
if (+value === value) {
n += 1;
}
}
return n ? d3Array.fsum(items, accessor) / n : void 0;
}
function cumsum(key) {
const keyFn = typeof key === "function" ? key : (d) => d[key];
return (items) => fcumsum(items, keyFn);
}
function roll(width, rollFn, options) {
const {partial = false, align = "right"} = options != null ? options : {};
const halfWidth = Math.floor(width / 2);
return (items) => {
return items.map((_, i) => {
const endIndex = align === "right" ? i : align === "center" ? i + halfWidth : i + width - 1;
if (!partial && (endIndex - width + 1 < 0 || endIndex >= items.length)) {
return void 0;
}
const startIndex = Math.max(0, endIndex - width + 1);
const itemsInWindow = items.slice(startIndex, endIndex + 1);
return rollFn(itemsInWindow, endIndex);
});
};
}
function lag(key, options) {
const keyFn = typeof key === "function" ? key : (d) => d[key];
const {n = 1, default: defaultValue} = options != null ? options : {};
return (items) => {
return items.map((_, i) => {
const lagItem = items[i - n];
return lagItem == null ? defaultValue : keyFn(lagItem, i, items);
});
};
}
function lead(key, options) {
const keyFn = typeof key === "function" ? key : (d) => d[key];
const {n = 1, default: defaultValue} = options != null ? options : {};
return (items) => {
return items.map((_, i) => {
const leadItem = items[i + n];
return leadItem == null ? defaultValue : keyFn(leadItem, i, items);
});
};
}
function rowNumber(options) {
var _a;
const startAt = (_a = options == null ? void 0 : options.startAt) != null ? _a : 0;
return (items) => {
return items.map((_, i) => i + startAt);
};
}
function min(key) {
const keyFn = typeof key === "function" ? key : (d) => d[key];
return (items) => d3Array.min(items, keyFn);
}
function max(key) {
const keyFn = typeof key === "function" ? key : (d) => d[key];
return (items) => d3Array.max(items, keyFn);
}
function mean$1(key) {
const keyFn = typeof key === "function" ? key : (d) => d[key];
return (items) => mean(items, keyFn);
}
function meanRate(numerator, denominator) {
const numeratorFn = typeof numerator === "function" ? numerator : (d) => d[numerator];
const denominatorFn = typeof denominator === "function" ? denominator : (d) => d[denominator];
return (items) => {
const numerator2 = d3Array.fsum(items, numeratorFn);
const denominator2 = d3Array.fsum(items, denominatorFn);
return rate(numerator2, denominator2);
};
}
function median(key) {
const keyFn = typeof key === "function" ? key : (d) => d[key];
return (items) => d3Array.median(items, keyFn);
}
function deviation(key) {
const keyFn = typeof key === "function" ? key : (d) => d[key];
return (items) => d3Array.deviation(items, keyFn);
}
function variance(key) {
const keyFn = typeof key === "function" ? key : (d) => d[key];
return (items) => d3Array.variance(items, keyFn);
}
function nDistinct(key, options = {}) {
const keyFn = typeof key === "function" ? key : (d) => d[key];
return (items) => {
const uniques = new Map();
let count = 0;
let i = 0;
for (const item of items) {
const value = keyFn(item, i++, items);
if (!uniques.has(value)) {
if (!options.includeUndefined && value === void 0 || options.includeNull === false && value === null) {
continue;
}
count += 1;
uniques.set(value, true);
}
}
return count;
};
}
function first(key) {
const keyFn = typeof key === "function" ? key : (d) => d[key];
return (items) => items.length ? keyFn(items[0]) : void 0;
}
function last(key) {
const keyFn = typeof key === "function" ? key : (d) => d[key];
return (items) => items.length ? keyFn(items[items.length - 1]) : void 0;
}
function startsWith(prefix, ignoreCase = true) {
return (items) => {
const regex = new RegExp(`^${prefix}`, ignoreCase ? "i" : void 0);
const keys = keysFromItems(items);
return keys.filter((d) => regex.test(d));
};
}
function endsWith(suffix, ignoreCase = true) {
return (items) => {
const regex = new RegExp(`${suffix}$`, ignoreCase ? "i" : void 0);
const keys = keysFromItems(items);
return keys.filter((d) => regex.test(d));
};
}
function contains(substring, ignoreCase = true) {
return (items) => {
const regex = new RegExp(substring, ignoreCase ? "i" : void 0);
const keys = keysFromItems(items);
return keys.filter((d) => regex.test(d));
};
}
function matches(regex) {
return (items) => {
const keys = keysFromItems(items);
return keys.filter((d) => regex.test(d));
};
}
function numRange(prefix, range, width) {
return (items) => {
const keys = keysFromItems(items);
const matchKeys = [];
for (let i = range[0]; i <= range[1]; ++i) {
const num = width == null ? i : new String("00000000" + i).slice(-width);
matchKeys.push(`${prefix}${num}`);
}
return keys.filter((d) => matchKeys.includes(d));
};
}
function negate(selectors) {
return (items) => {
let keySet = new Set();
for (const selector of singleOrArray(selectors)) {
if (typeof selector === "function") {
const keys2 = selector(items);
for (const key of keys2) {
keySet.add(key);
}
} else {
keySet.add(selector);
}
}
const keys = Array.from(keySet).map((key) => `-${key}`);
return keys;
};
}
exports.TMath = math;
exports.addItems = addRows;
exports.addRows = addRows;
exports.arrange = arrange;
exports.asc = asc;
exports.complete = complete;
exports.contains = contains;
exports.count = count;
exports.cumsum = cumsum;
exports.debug = debug;
exports.desc = desc;
exports.deviation = deviation;
exports.distinct = distinct;
exports.endsWith = endsWith;
exports.everything = everything;
exports.expand = expand;
exports.fill = fill;
exports.filter = filter;
exports.first = first;
exports.fixedOrder = fixedOrder;
exports.fullJoin = fullJoin;
exports.fullSeq = fullSeq;
exports.fullSeqDate = fullSeqDate;
exports.fullSeqDateISOString = fullSeqDateISOString;
exports.groupBy = groupBy;
exports.innerJoin = innerJoin;
exports.lag = lag;
exports.last = last;
exports.lead = lead;
exports.leftJoin = leftJoin;
exports.map = map;
exports.matches = matches;
exports.max = max;
exports.mean = mean$1;
exports.meanRate = meanRate;
exports.median = median;
exports.min = min;
exports.mutate = mutate;
exports.mutateWithSummary = mutateWithSummary;
exports.n = n;
exports.nDistinct = nDistinct;
exports.negate = negate;
exports.numRange = numRange;
exports.pick = select;
exports.pivotLonger = pivotLonger;
exports.pivotWider = pivotWider;
exports.rate = rate$1;
exports.rename = rename;
exports.replaceNully = replaceNully;
exports.roll = roll;
exports.rowNumber = rowNumber;
exports.select = select;
exports.slice = slice;
exports.sliceHead = sliceHead;
exports.sliceMax = sliceMax;
exports.sliceMin = sliceMin;
exports.sliceSample = sliceSample;
exports.sliceTail = sliceTail;
exports.sort = arrange;
exports.startsWith = startsWith;
exports.sum = sum;
exports.summarize = summarize;
exports.summarizeAll = summarizeAll;
exports.summarizeAt = summarizeAt;
exports.summarizeIf = summarizeIf;
exports.tally = tally;
exports.tidy = tidy;
exports.total = total;
exports.totalAll = totalAll;
exports.totalAt = totalAt;
exports.totalIf = totalIf;
exports.transmute = transmute;
exports.variance = variance;
exports.vectorSeq = vectorSeq;
exports.vectorSeqDate = vectorSeqDate;
exports.when = when;
Object.defineProperty(exports, '__esModule', { value: true });
})));
//# sourceMappingURL=tidy.js.map