function fillData2(data, keyFields, valueFields, options = {}) {
if (keyFields == undefined || keyFields.length == 0) {
throw "No key fields specified";
}
if (valueFields == undefined || valueFields.length == 0) {
throw "No value fields specified";
}
const modes = ["filler", "previous"];
const { mode = "filler", filler = 0, timeField = null } = options;
if (!modes.includes(mode)) {
throw `Mode must be either "filler" or "previous", not ${mode}`;
}
if (mode === "previous" && !timeField) {
throw `timeField must be defined in "previous" mode`;
}
if (timeField !== null && !keyFields.includes(timeField)) {
throw `timeField must be on of the fields set in keyFields, not ${timeField}`;
}
if (typeof keyFields !== "object") {
keyFields = [keyFields];
}
if (typeof valueFields !== "object") {
valueFields = [valueFields];
}
const keySets = makeKeySets(data, keyFields);
console.log("a", keySets);
if (timeField) {
keySets.set(timeField, Array.from(keySets.get(timeField)).sort());
}
// next, make key maps from the original data
const dataKeyMap = makeExistingDataKeyMap(data, keyFields);
console.log("b", dataKeyMap);
// used to tell how many entries back in the cartesian
// product array the previous time value is
const entriesPerTime = Array.from(keySets)
.filter((d) => d[0] !== timeField)
.map((d) => d[1].size)
.reduce((r, g) => r * g);
// get the cartesian product of all keyProp values
// for those combinations that exist, use existing data
// for those combinations that don't, use filler data
const productArray = product(...[...keySets.values()]);
return productArray.map((k, i) => {
// if the entry for that key set already exists, return it
const entry = dataKeyMap.get(k.toString());
if (entry) {
return entry;
}
// if the entry does not exist, create it
if (mode === "filler") {
// in filler mode, just use the filler value(s)
return makeFillerEntry(keyFields, k, valueFields, filler);
} else {
// in previous mode, get the previous time value(s)
if (!productArray[i - entriesPerTime]) {
// if the previous time does not exist, create a new filler entry
const firstEntry = makeFillerEntry(keyFields, k, valueFields, filler);
// add it to the data key map for future times to reference
dataKeyMap.set(k.toString(), firstEntry);
return firstEntry;
}
const prevData = dataKeyMap.get(
productArray[i - entriesPerTime].toString()
);
const newEntry = makeFillerEntry(
keyFields,
k,
valueFields,
valueFields.map((d) => prevData[d])
);
dataKeyMap.set(k.toString(), newEntry);
return newEntry;
}
});
}