2

I have an array of objects with 3 levels. I want to target the innermost child with property 'code'. If code is present, then update the child as well as the parent object with selected: true. If the property already is true, then sending in the same value should set the child to selected: false (toggle) This is what I have going so far. If selected is true for a child, sending in another code should set selected: true for the corresponding child and selected: false for the child which had true for selected property.

Also, if selected is true for a child, defaultCollapsed property should be false for the child as well as parent and grandparent. Ifs elected is false for a child, defaultCollapsed property should be true for the child as well as parent and grandparent

const data = [{
    "label": "Grand Parent 1",
    "index": 0,
    "code": "GRAND_PARENT_1",
    "defaultCollapsed": true,
    "items": [{
        "id": 1,
        "items": [{
            "id": 100,
            "label": "Child 1",
            "url": "#CHILD_1",
            "code": "CHILD_1",
          },
          {
            "id": 200,
            "label": "Child 2",
            "url": "#CHILD_2",
            "code": "CHILD_2"
          },
          {
            "id": 300,
            "label": "Child 3",
            "url": "#CHILD_3",
            "code": "CHILD_3"
          },
          {
            "id": 400,
            "label": "Child 4",
            "url": "#CHILD_4",
            "code": "CHILD_4"
          }
        ],
        "defaultCollapsed": true,
        "label": "Parent 1",
        "selected": true,
      },
      {
        "id": 2,
        "items": [],
        "defaultCollapsed": true,
        "label": "Parent 2"
      },
      {
        "id": 3,
        "items": [],
        "defaultCollapsed": true,
        "label": "Parent 3"
      },
      {
        "id": 4,
        "items": [],
        "defaultCollapsed": true,
        "label": "Parent 4"
      }
    ]
  },
  {
    "label": "Grand Parent 2",
    "index": 1,
    "code": "GRAND_PARENT_2",
    "defaultCollapsed": true,
    "items": []
  },
  {
    "label": "Grand Parent 3",
    "index": 2,
    "code": "GRAND_PARENT_3",
    "defaultCollapsed": true,
    "items": []
  }
]

function select(items, key, value) {
  if (!Array.isArray(items)) {
    return false;
  }

  for (const item of items) {
    if (item.code === value || select(item.items, key, value)) {
      item.selected = !item.selected;
      item.defaultCollapsed = false;
      return true;
    }
  }

  return false;
}

function reset(items) {
  if (!Array.isArray(items)) {
    return;
  }

  for (const item of items) {
    if (item.selected) {
      reset(item.items);
      item.selected = false;
      break;
    }
  }
}

function resetAndSelect(data, key, value) {
  reset(data);
  select(data, key, value);
}

resetAndSelect(data, 'code', 'CHILD_1')

console.log('CHILD_1',data)

resetAndSelect(data, 'code', 'CHILD_2')

console.log('CHILD_2',data)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>

If code is CHILD_1:

[{
    "label": "Grand Parent 1",
    "index": 0,
    "code": "GRAND_PARENT_1",
    "defaultCollapsed": true,
    "selected": true,
    "items": [{
        "id": 1,
        "items": [{
            "id": 100,
            "label": "Child 1",
            "url": "#CHILD_1",
            "code": "CHILD_1",
            "selected": true,
          },
          {
            "id": 200,
            "label": "Child 2",
            "url": "#CHILD_2",
            "code": "CHILD_2"
          },
          {
            "id": 300,
            "label": "Child 3",
            "url": "#CHILD_3",
            "code": "CHILD_3"
          },
          {
            "id": 400,
            "label": "Child 4",
            "url": "#CHILD_4",
            "code": "CHILD_4"
          }
        ],
        "defaultCollapsed": false,
        "label": "Parent 1",
        "selected": true,
      },
      {
        "id": 2,
        "items": [],
        "defaultCollapsed": true,
        "label": "Parent 2"
      },
      {
        "id": 3,
        "items": [],
        "defaultCollapsed": true,
        "label": "Parent 3"
      },
      {
        "id": 4,
        "items": [],
        "defaultCollapsed": true,
        "label": "Parent 4"
      }
    ]
  },
  {
    "label": "Grand Parent 2",
    "index": 1,
    "code": "GRAND_PARENT_2",
    "defaultCollapsed": true,
    "items": []
  },
  {
    "label": "Grand Parent 3",
    "index": 2,
    "code": "GRAND_PARENT_3",
    "defaultCollapsed": true,
    "items": []
  }
]

If the above output is the actual data, and the code is CHILD_1 again, I need to toggle the selected property of the child as well the corresponding parents.

[{
    "label": "Grand Parent 1",
    "index": 0,
    "code": "GRAND_PARENT_1",
    "defaultCollapsed": true,
    "selected": false,
    "items": [{
        "id": 1,
        "items": [{
            "id": 100,
            "label": "Child 1",
            "url": "#CHILD_1",
            "code": "CHILD_1",
            "selected": false,
          },
          {
            "id": 200,
            "label": "Child 2",
            "url": "#CHILD_2",
            "code": "CHILD_2"
          },
          {
            "id": 300,
            "label": "Child 3",
            "url": "#CHILD_3",
            "code": "CHILD_3"
          },
          {
            "id": 400,
            "label": "Child 4",
            "url": "#CHILD_4",
            "code": "CHILD_4"
          }
        ],
        "defaultCollapsed": false,
        "label": "Parent 1",
        "selected": false,
      },
      {
        "id": 2,
        "items": [],
        "defaultCollapsed": true,
        "label": "Parent 2"
      },
      {
        "id": 3,
        "items": [],
        "defaultCollapsed": true,
        "label": "Parent 3"
      },
      {
        "id": 4,
        "items": [],
        "defaultCollapsed": true,
        "label": "Parent 4"
      }
    ]
  },
  {
    "label": "Grand Parent 2",
    "index": 1,
    "code": "GRAND_PARENT_2",
    "defaultCollapsed": true,
    "items": []
  },
  {
    "label": "Grand Parent 3",
    "index": 2,
    "code": "GRAND_PARENT_3",
    "defaultCollapsed": true,
    "items": []
  }
]
3
  • what is with other items and a possible selected: true or selected: false? Commented Nov 28, 2019 at 19:46
  • By default as per out post from stackoverflow.com/posts/comments/104423033, if code is CHILD_1, selected property should be set to true on grandparent, parent and child and defaultCollapsed should be set to false on grandparent, parent and child. If CHILD_1 already has selected: true, sending CHILD_1 again should set selected property to false on grandparent, parent and child and defaultCollapsed should be set to true on grandparent, parent and child. Another case is that when CHILD_1 is true and CHILD_2 is sent, only CHILD_1 selected is false. But parent and grandparent and CHILD_2 Commented Nov 28, 2019 at 19:50
  • should be true and defaultCollapsed should be false Commented Nov 28, 2019 at 19:51

2 Answers 2

1

You can do this by creating recursive function using forEach loop and passing array of parents down with recursion so that you can update its selected property.

const data = [{"label":"Grand Parent 1","index":0,"code":"GRAND_PARENT_1","defaultCollapsed":true,"items":[{"id":1,"items":[{"id":100,"label":"Child 1","url":"#CHILD_1","code":"CHILD_1"},{"id":200,"label":"Child 2","url":"#CHILD_2","code":"CHILD_2"},{"id":300,"label":"Child 3","url":"#CHILD_3","code":"CHILD_3"},{"id":400,"label":"Child 4","url":"#CHILD_4","code":"CHILD_4"}],"defaultCollapsed":true,"label":"Parent 1","selected":true},{"id":2,"items":[],"defaultCollapsed":true,"label":"Parent 2"},{"id":3,"items":[],"defaultCollapsed":true,"label":"Parent 3"},{"id":4,"items":[],"defaultCollapsed":true,"label":"Parent 4"}]},{"label":"Grand Parent 2","index":1,"code":"GRAND_PARENT_2","defaultCollapsed":true,"items":[]},{"label":"Grand Parent 3","index":2,"code":"GRAND_PARENT_3","defaultCollapsed":true,"items":[]}]

function update(data, key, value, parents = []) {
  data.forEach(e => {
    if (e[key] === value) {
      if (parents.length) {
        e.selected = !e.selected;

        parents.forEach(p => {
          p.defaultCollapsed = !e.selected
          p.selected = e.selected
        })

        data.forEach(s => {
          if (s != e && s.selected) s.selected = false;
        });
      }
    }

    if (e.items && e.items.length) {
      update(e.items, key, value, [...parents, e])
    }
  })
}

update(data, 'code', 'CHILD_4')
update(data, 'code', 'CHILD_4')
update(data, 'code', 'CHILD_2')
update(data, 'code', 'CHILD_1')
console.log(data)

Sign up to request clarification or add additional context in comments.

13 Comments

This doesnt add selected property to the parent.
It does add to the parent, do you mean grand-parent?
Yes, to parent and grand parent as well
Didn't you say that you want to keep parent selected value true even when you set child selected value to false?
I did, I rephrased the question before you posted your answer. My bad. Sorry about that. My requirement wasnt clear enough.
|
1

You could toggle the target node and leave the rest as true, for the path to the item.

function update(array, value) {
    var found = false;
    array.forEach(o => {
        var sub = update(o.items || [], value),
            check = o.code === value;

        if (check) {
            o.selected = !o.selected
            found = o.selected;
        } else {
            o.selected = sub;
            if (sub) found = o.selected;
        }
        o.defaultCollapsed = !o.selected;
    });
    return found;
}

var data = [{ label: "Grand Parent 1", index: 0, code: "GRAND_PARENT_1", defaultCollapsed: true, items: [{ id: 1, items: [{ id: 100, label: "Child 1", url: "#CHILD_1", code: "CHILD_1" }, { id: 200, label: "Child 2", url: "#CHILD_2", code: "CHILD_2" }, { id: 300, label: "Child 3", url: "#CHILD_3", code: "CHILD_3" }, { id: 400, label: "Child 4", url: "#CHILD_4", code: "CHILD_4" }], defaultCollapsed: false, label: "Parent 1" }, { id: 2, items: [], defaultCollapsed: true, label: "Parent 2" }, { id: 3, items: [], defaultCollapsed: true, label: "Parent 3" }, { id: 4, items: [], defaultCollapsed: true, label: "Parent 4" }] }, { label: "Grand Parent 2", index: 1, code: "GRAND_PARENT_2", defaultCollapsed: true, items: [] }, { label: "Grand Parent 3", index: 2, code: "GRAND_PARENT_3", defaultCollapsed: true, items: [] }];

update(data, 'CHILD_1');
console.log(data);
update(data, 'CHILD_1');
console.log(data);
.as-console-wrapper { max-height: 100% !important; top: 0; }

3 Comments

The parent still has selected true in the second output. The child as well the parent selected property should be toggled if the same property is passed in again
Perfect. One small change I need is that I need to update defaultCollapsed to false when the child selected is set to true. If child selected is false, defaultCollapsed on grandparent, parent and child should be set to true.
found = (o.selected = !o.selected); How can I fix this multiassign?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.