Home > Enterprise >  How to split recursive array into two categories based on specific field and also get the parent pat
How to split recursive array into two categories based on specific field and also get the parent pat

Time:01-20

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);

  • Related