@ndbx/runtime
Version:
The `@ndbx/runtime` package provides a runtime environment to embed NodeBox visualizations directly into React applications. NodeBox is a powerful tool for creating interactive and generative visualizations, and this runtime allows you to integrate those
139 lines (122 loc) • 3.92 kB
JavaScript
/**
* Explode nested arrays in data by expanding each array element into a separate record,
* duplicating non-array fields.
*
* @category Data Manipulation
*/
export default function (node) {
const dataIn = node.tableIn({ name: "dataIn", label: "data" });
node.pushSection({ name: "General" });
const sourceTypeIn = node.stringIn({
name: "sourceType",
label: "Source type",
value: "input",
choices: [
["input", "Input port"],
["attribute", "Attribute"],
["string", "String"],
],
});
node.popSection();
// Section: Explode options
node.pushSection({ name: "Explode options" });
const attrIn = node.stringIn({
name: "attribute",
label: "Attribute to explode",
value: "",
});
node.popSection();
// Section: Source settings
node.pushSection({ name: "Source" });
const sourceIn = node.stringIn({
name: "sourceString",
label: "Source string",
widget: "TEXT",
value: "",
});
node.popSection();
// Function to explode data based on the array in the specified attribute
function explodeData(data, attribute = "") {
let result = [];
// If no attribute is provided, explode the entire input object
if (!attribute) {
data.forEach((item) => {
// Check for any arrays in the object and explode them
const exploded = Object.keys(item).reduce((acc, key) => {
const value = item[key];
if (Array.isArray(value)) {
// Duplicate the object for each array element
value.forEach((arrayItem) => {
const newItem = { ...item, [key]: arrayItem };
acc.push(newItem);
});
} else {
// No array, just keep the current value
acc.push(item);
}
return acc;
}, []);
result.push(...exploded);
});
} else {
// Explode based on the provided attribute
data.forEach((item) => {
const value = item[attribute];
if (Array.isArray(value)) {
// For each element in the array, create a new object
value.forEach((arrayItem) => {
const newItem = { ...item, [attribute]: arrayItem };
result.push(newItem);
});
} else {
// If the attribute is not an array, just keep the original object
result.push(item);
}
});
}
return result;
}
const dataOut = node.tableOut({ name: "dataOut", label: "Exploded Data" });
node.onRender = () => {
const sourceType = sourceTypeIn.value;
const attribute = attrIn.value;
let dataToExplode;
// Determine the source data based on sourceType
switch (sourceType) {
case "input":
// Use the input port value as the source data
dataToExplode = dataIn.value;
break;
case "attribute":
// Use the specified attribute of the input data
if (dataIn.value && Array.isArray(dataIn.value)) {
dataToExplode = dataIn.value.flatMap((row) => row[attribute]);
}
break;
case "string":
// Use the provided source string as the data
const sourceString = sourceIn.value;
try {
dataToExplode = [JSON.parse(sourceString)]; // Parse string as JSON
} catch (e) {
// If parsing fails, log an error and return
dataOut.set([]);
return;
}
break;
default:
node.error("Invalid source type.");
dataOut.set([]);
return;
}
// Ensure valid data to explode
if (!dataToExplode || !Array.isArray(dataToExplode)) {
dataOut.set([]); // Set an empty array if no valid data
return;
}
// Call explodeData to explode the determined source data
const explodedData = explodeData(dataToExplode, attribute);
// Output the exploded data
dataOut.set(explodedData);
};
}