2

I would like to update the object with values after a recursive search finds a particular node.

Where do I need to add logic to achieve this?

I would like to get the first found object from the nested array of objects and update the data with selected:true based on the iterations to get the value showTree: true.

Function:

let findDeep = function(data, label) {
  return data.filter(function(e) {
    if (e.label.includes(label)) {
      data.map(el=> el.selected= "true"); // logic to select the first found value
      return e;
    } 
    else if (e.item) 
    //logic for showTree: true
    return findDeep(e.item, label);
  });
};

Data:

let testData = [
  {
    id: 1,
    label: 'parent1',
    item: [
      {
        id: 21,
        label: 'child1',
        item: [
          {
            id: 211,
            label: 'child31',
            item: [
              {
                id: 2111,
                label: 'child2211',
                item: [
                  {
                    id: 21111,
                    label: 'child22111'
                  }
                ]
              }
            ]
          },
          {
            id: 222,
            label: 'child32'
          }
        ]
      },
      {
        id: 22,
        label: 'child2',
        item: [
          {
            id: 221,
            label: 'child421',
            item: [
              {
                id: 2211,
                label: 'child2211'
              }
            ]
          },
          {
            id: 222,
            label: 'child222'
          }
        ]
      }
    ]
  },
  {
    id: 2,
    label: 'parent2',
    item: [
      {
        id: 21,
        label: 'child2',
        item: [
          {
            id: 511,
            label: 'child51',
            item: [
              {
                id: 5111,
                label: 'child5211',
                item: [
                  {
                    id: 51111,
                    label: 'child52111'
                  }
                ]
              }
            ]
          },
          {
            id: 522,
            label: 'child352'
          }
        ]
      }
    ]
  }
];

I would like to achieve something in the output

console.log(findDeep(testData, 'child3')[0]);

//output list
[
   {
      "id":1,
      "label":"parent1",
      "showTree": true,
      "item":[
         {
            "id":21,
            "label":"child1",
            "showTree": true,
            "item":[
               {
                  "id":211,
                  "label":"child31",
                  "selected" true,
                  "item":[
                     {
                        "id":2111,
                        "label":"child2211",
                        "item":[
                           {
                              "id":21111,
                              "label":"child22111"
                           }
                        ]
                     }
                  ]
               },
               {
                  "id":222,
                  "label":"child32"
               }
            ]
         },
         {
            "id":22,
            "label":"child2",
            "item":[
               {
                  "id":221,
                  "label":"child421",
                  "item":[
                     {
                        "id":2211,
                        "label":"child2211"
                     }
                  ]
               },
               {
                  "id":222,
                  "label":"child222"
               }
            ]
         }
      ]
   },
   {
      "id":2,
      "label":"parent2",
      "item":[
         {
            "id":21,
            "label":"child2",
            "item":[
               {
                  "id":511,
                  "label":"child51",
                  "item":[
                     {
                        "id":5111,
                        "label":"child5211",
                        "item":[
                           {
                              "id":51111,
                              "label":"child52111"
                           }
                        ]
                     }
                  ]
               },
               {
                  "id":522,
                  "label":"child352"
               }
            ]
         }
      ]
   }
]

//ouptput selected value

{
   "id":211,
   "label":"child31",
   "selected":true,
   "item":[
      {
         "id":2111,
         "label":"child2211",
         "item":[
            {
               "id":21111,
               "label":"child22111"
            }
         ]
      }
   ]
}
ggorlen
  • 44,755
  • 7
  • 76
  • 106
app
  • 197
  • 8
  • 24
  • 1
    The node you're targeting is `child31` but your function call is `child3`. Was that a typo or do you really want to use a prefix of the string or substring logic here? – ggorlen Jul 05 '21 at 18:48
  • @ggorlen No, I want to use "includes" logic so the first searched term will be selected – app Jul 05 '21 at 18:49

2 Answers2

2

You can do a normal tree search, modify the found property, then pass true back up the tree towards the root, applying showTree: true along the way.

Note that this is an in-place approach, so the semantics are a little different than you show on your call. That's probably most appropriate for an algorithm like this that is just modifying a few properties on an existing structure rather than reallocating the whole thing from scratch. It's an antipattern to return the original structure for in-place algorithms as .sort() and .reverse() do for the purposes of chaining -- this can lead to surprising and subtle bugs.

const expandPath = (nodes, targetLabel) => {
  for (const node of nodes || []) {
    if (node.label.includes(targetLabel)) {
      return node.selected = true;
    }
    else if (expandPath(node.item, targetLabel)) {
      return node.showTree = true;
    }
  }
};

const testData = [ { id: 1, label: 'parent1', item: [ { id: 21, label: 'child1', item: [ { id: 211, label: 'child31', item: [ { id: 2111, label: 'child2211', item: [ { id: 21111, label: 'child22111' } ] } ] }, { id: 222, label: 'child32' } ] }, { id: 22, label: 'child2', item: [ { id: 221, label: 'child421', item: [ { id: 2211, label: 'child2211' } ] }, { id: 222, label: 'child222' } ] } ] }, { id: 2, label: 'parent2', item: [ { id: 21, label: 'child2', item: [ { id: 511, label: 'child51', item: [ { id: 5111, label: 'child5211', item: [ { id: 51111, label: 'child52111' } ] } ] }, { id: 522, label: 'child352' } ] } ] } ];

expandPath(testData, "child3");
console.log(testData[0]);

Note that the added properties are at the bottom of each node.

Also, in your original attempt, please avoid using map for in-place operations -- its purpose is to allocate a new array, not modify it. Use forEach instead.

ggorlen
  • 44,755
  • 7
  • 76
  • 106
  • Just one question... is there any way I can get the count of occurrences as I want to show Example: "1 of 20 matches" – app Jul 06 '21 at 14:51
  • expandPath(testData, 'child421'); when I pass this it shows nodes is not iterable – app Jul 06 '21 at 16:00
  • 1
    Right, I see leaf nodes don't have `.item`. Updated. Re: all matches, did you want a count of how many "selected: true" keys wind up being added for a particular call? You could make that a separate pass, usual tree search that returns the count of nodes with that property. It could be baked into one by returning a count rather than true/flase. – ggorlen Jul 06 '21 at 17:25
  • Actually, I want a function in which the user can check the next search records and previous ones. – app Jul 06 '21 at 17:28
  • I added question here with explanations .. https://stackoverflow.com/questions/68274003/update-the-count-of-searched-results-and-change-the-selection-based-on-the-numbe – app Jul 06 '21 at 17:29
  • 1
    OK, thanks for asking a new question, I'll take a peek -- I didn't realize you wanted more than 1 item to be selected. The current algorithm I show just expands the first match it finds, then stops. – ggorlen Jul 06 '21 at 17:35
  • Thanks, Ggorlen.. please guide me whenever you can... – app Jul 08 '21 at 13:53
1

I would suggest to use string methods, like

- `includes` or
- `startsWith`

along with the wanted parameter.

search = (array, type, value) => array.some(o => {
    if (o.label[type](value)) return o.selected = true;
    return search(o.item || [], type, value);
})
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
  • Thanks a lot, Nina, Just one question.. How I can update the tree based on the selected value? I would like to make showTree: true if I need to make iterations to reach at the selected value – app Jul 05 '21 at 18:55
  • [{"id":1,"label":"parent1","showTree":true,"item":[{"id":21,"label":"child1","showTree":true,"item":[{"id":211,"label":"child31","item":[{"id":2111,"label":"child2211","item":[{"id":21111,"label":"child22111"}]}]},{"id":222,"label":"child32"}],"selected":true},{"id":22,"label":"child2","item":[{"id":221,"label":"child421","item":[{"id":2211,"label":"child2211"}]},{"id":222,"label":"child222"}]}]},{"id":2,"label":"parent2","item":[{"id":21,"label":"child2","item":[{"id":511,"label":"child51","item":[{"id":5111,"label":"child5211","item":[]}]},{"id":522,"label":"child352"}]}]}] – app Jul 05 '21 at 18:57