Home > front end >  filter nested array of objects depending on children conditions
filter nested array of objects depending on children conditions

Time:06-10

there is a tree of categories. its an array of objects (in this case 1 ob ject with children property. children property is an array containing or not containing other categories objects and so on ). Each object also has a property "disabled". It can be either true or false. The starting point is that some bottommost children "disabled" are set to true. In this case all of them are set to true. The task is to find parents ids which disabled have to be set to true if all of its children disabled are set to true. In this particular case all parents ids have to be found because all bottommost children disabled are set to true. My function returns only lowest level of parents. What am I doing wrong?

let parentIdsToDisable = [];
    function findParentIdsToDisable(tree) {
      tree.forEach((category) => {
        if (category.children.length > 0) {
          if (category.children.every((child) => child.disabled === true)) {
            category.disabled = true;
            parentIdsToDisable.push(category.id);
          }
        }
        findParentIdsToDisable(category.children);
      });
  }

const categories = [
  {
    id: 69,
    name: 'Прикраси',
    key: 'prykrasy',
    description: 'Прикраси',
    disabled: false,
    mpath: '69.',
    children: [
       {
        id: 70,
        name: 'Аксесуари',
        key: 'aksesyary-dlya-prykras',
        description: 'Аксесуари для прикрас',
        disabled: true,
        mpath: '69.70.',
        children: []
      },
       {
        id: 72,
        name: 'Ювелірні вироби',
        key: 'uvelirni-vyroby',
        description: 'Ювелірні вироби',
        disabled: false,
        mpath: '69.72.',
        children: [
          {
            id: 73,
            name: 'Срібло',
            key: 'vyroby-iz-sribla',
            description: 'Ювелірні вироби із срібла',
            disabled: true,
            mpath: '69.72.73.',
            children: []
          }
        ]
      },
       {
        id: 71,
        name: 'Біжутерія',
        key: 'bizhuteriya',
        description: 'Біжутерія',
        disabled: true,
        mpath: '69.71.',
        children: []
      }
    ]
  }
]

CodePudding user response:

You need to place the recursive call first, and also save the current item's id if it itself is disabled.

function findParentIdsToDisable(tree) {
  tree.forEach((category) => {
    findParentIdsToDisable(category.children);
    if (category.disabled) {
      parentIdsToDisable.push(category.id);
    }
    if (category.children.length > 0) {
      if (category.children.every((child) => child.disabled === true)) {
        category.disabled = true;
        parentIdsToDisable.push(category.id);
      }
    }
  });
}

This results in [ 70, 73, 72, 71, 69 ] for your data.

CodePudding user response:

You need to recurse and apply the logic to each child before checking whether each child is disabled to see whether to disable the parent or not.

const categories=[{id:69,name:"Прикраси",key:"prykrasy",description:"Прикраси",disabled:!1,mpath:"69.",children:[{id:70,name:"Аксесуари",key:"aksesyary-dlya-prykras",description:"Аксесуари для прикрас",disabled:!0,mpath:"69.70.",children:[]},{id:72,name:"Ювелірні вироби",key:"uvelirni-vyroby",description:"Ювелірні вироби",disabled:!1,mpath:"69.72.",children:[{id:73,name:"Срібло",key:"vyroby-iz-sribla",description:"Ювелірні вироби із срібла",disabled:!0,mpath:"69.72.73.",children:[]}]},{id:71,name:"Біжутерія",key:"bizhuteriya",description:"Біжутерія",disabled:!0,mpath:"69.71.",children:[]}]}];

const disableObjectIfChildrenDisabled = (parent) => {
    for (const child of parent.children ?? []) {
        disableObjectIfChildrenDisabled(child);
    }
    if (parent.children?.every(child => child.disabled)) {
        parent.disabled = true;
    }
};
for (const parent of categories) {
    disableObjectIfChildrenDisabled(parent);
}
console.log(categories);

CodePudding user response:

Another approach would be to simply make a copy of the data with the changes already applied. This may or may not meet your needs, but it seems simpler than collecting the ids that need to change and doing something with them afterward.

This is a fairly simple recursive implementation of that idea:

const bubbleDisabled = (xs) =>
  xs .map (({children, disabled, kids = bubbleDisabled (children), ...rest}) => ({
    ...rest,
    disabled: disabled || (kids .length > 0 && kids .every (k => k .disabled)),
    children: kids
  }))

const categories = [{id: 69, name: "Прикраси", key: "prykrasy", description: "Прикраси", disabled: false, mpath: "69.", children: [{id: 70, name: "Аксесуари", key: "aksesyary-dlya-prykras", description: "Аксесуари для прикрас", disabled: true, mpath: "69.70.", children: []}, {id: 72, name: "Ювелірні вироби", key: "uvelirni-vyroby", description: "Ювелірні вироби", disabled: false, mpath: "69.72.", children: [{id: 73, name: "Срібло", key: "vyroby-iz-sribla", description: "Ювелірні вироби із срібла", disabled: true, mpath: "69.72.73.", children: []}]}, {id: 71, name: "Біжутерія", key: "bizhuteriya", description: "Біжутерія", disabled: true, mpath: "69.71.", children: []}]}]

console .log (bubbleDisabled (categories))
.as-console-wrapper {max-height: 100% !important; top: 0}

We need the length check because every will (rightly) return true for an empty array, and I assume we wouldn't want to bubble that up to its parents. Also we use disabled || because I assume if a node is already disabled, we wouldn't want to enable it based on the children. If either assumption is wrong, you can simplify the assignment of disabled.

  • Related