I have confusion on recursive function to split recursive array into another recursive arrays. Let say I have an item and inside it there is packaged items (or children), and the children also have an item and children, there's no limitation about the recursion level.
The problem
I need to separate the paths of package item object that has isOptional true
and paths of isOptional false
. The function should return 2 categorized array and the value inside the array must be recursive just like the input structure. Please check the diagram below
Here's input example
const product = {
name: "Product 1",
packagedItems: [
{
id: 1,
isOptional: false,
item: {
name: "1#1",
packagedItems: [
{
id: 3,
isOptional: false,
item: {
name: "2#1",
packagedItems: [
{
id: 5,
isOptional: false,
item: {
name: "3#1",
packagedItems: []
}
},
{
id: 6,
isOptional: true,
item: {
name: "3#2",
packagedItems: []
}
}
]
}
}
]
}
},
{
id: 2,
isOptional: false,
item: {
name: "1#2",
packagedItems: [
{
id: 4,
isOptional: false,
item: {
name: "2#2",
packagedItems: [
{
id: 7,
isOptional: true,
item: {
name: "3#3",
packagedItems: []
}
},
{
id: 8,
isOptional: true,
item: {
name: "3#4",
packagedItems: []
}
}
]
}
}
]
}
}
]
};
Here's the diagram enter image description here
What I've tried is only able to get parent's name but not building the same structure as the input
const getParents = (
packagedItems: PackagedItem[],
ancestors: (string | PackagedItem)[] = []
): any => {
for (let pack of packagedItems) {
if (pack.isOptional && !pack.item.packagedItems.length) {
return ancestors.concat(pack);
}
const found = getParents(
pack.item.packagedItems,
ancestors.concat(pack.item.name)
);
if (found) {
return found;
}
}
return undefined;
};
console.log(getParents(product.packagedItems));
only return
[
"1#1",
"2#1",
{
id: 6
isOptional: true
item: Object
}
]
Expected result would be two arrays like this.
const optionalTrue = [
{
id: 1,
isOptional: false,
item: {
name: "1#1",
packagedItems: [
{
id: 3,
isOptional: false,
item: {
name: "2#1",
packagedItems: [
{
id: 6,
isOptional: true,
item: {
name: "3#2",
packagedItems: []
}
}
]
}
}
]
}
},
{
id: 2,
isOptional: false,
item: {
name: "1#2",
packagedItems: [
{
id: 4,
isOptional: false,
item: {
name: "2#2",
packagedItems: [
{
id: 7,
isOptional: true,
item: {
name: "3#3",
packagedItems: []
}
},
{
id: 8,
isOptional: true,
item: {
name: "3#4",
packagedItems: []
}
}
]
}
}
]
}
}
];
const optionalFalse = [
{
id: 1,
isOptional: false,
item: {
name: "1#1",
packagedItems: [
{
id: 3,
isOptional: false,
item: {
name: "2#1",
packagedItems: [
{
id: 5,
isOptional: false,
item: {
name: "3#1",
packagedItems: []
}
}
]
}
}
]
}
}
];
CodePudding user response:
Your function is going in the right direction, but the pack is a leaf, and matches the category, you should not continue with the recursive call. Also the collection into the ancestors
array will not work well as you collect either pack
or name
in it.
Here is an implementation that gives the output you intended:
function filterOnLeaf(item, optional) {
return {
...item,
packagedItems: item.packagedItems.map(package => {
if (!package.item.packagedItems.length) { // Leaf
if (package.isOptional != optional) return null;
} else { // Internal
package = {
...package,
item: filterOnLeaf(package.item, optional)
};
if (!package.item.packagedItems.length) return null;
}
return package;
}).filter(Boolean)
};
}
const product = {name: "Product 1",packagedItems: [{id: 1,isOptional: false,item: {name: "1#1",packagedItems: [{id: 3,isOptional: false,item: {name: "2#1",packagedItems: [{id: 5,isOptional: false,item: {name: "3#1",packagedItems: []}},{id: 6,isOptional: true,item: {name: "3#2",packagedItems: []}}]}}]}},{id: 2,isOptional: false,item: {name: "1#2",packagedItems: [{id: 4,isOptional: false,item: {name: "2#2",packagedItems: [{id: 7,isOptional: true,item: {name: "3#3",packagedItems: []}},{id: 8,isOptional: true,item: {name: "3#4",packagedItems: []}}]}}]}}]};
const optionalTrue = filterOnLeaf(product, true);
const optionalFalse = filterOnLeaf(product, false);
console.log("optional is true:");
console.log(optionalTrue);
console.log("optional is false:");
console.log(optionalFalse);