TransWikia.com

Traverse JavaScript array and dynamically find deep nested values

Stack Overflow Asked by user12097954 on January 5, 2022

I am currently manually looping through an array and doing deeper and deeper nested loops to compare values, but I am curious if there is any way to do this search automatically? I need to find deep nested arrays, compare 1 or 2 values and then also be able to modify the values.

Example array.

searchableArray = [];

searchableArray.push({id: 3, type: 'some-type', text: 'text', nestedElements: [{id: 4, type: 'some-type', text: 'other text', nestedElements: []}, {id: 5, type: 'another-type',  text: 'more text', nestedElements: []}]})
searchableArray.push({id: 6, type: 'other-type', text: 'text', nestedElements: [{id: 7, type: 'other-type', text: 'other text', nestedElements: []}, {id: 8, type: 'another-type',  text: 'more text', nestedElements: []}]})
searchableArray.push({id: 9, type: 'another-type', text: 'text', nestedElements: [{id: 10, type: 'another-type', text: 'other text', nestedElements: []}, {id: 11, type: 'another-type',  text: 'more text', nestedElements: []}]})

Basically I need to search for id (It will be unique throughout the entire array and object, but could be nested various levels deep inside an object inside another array. But will always be called "nestedElements".

I need to be able to find the ID and then modify the object that ID belongs too and put it back in the array I am using.

Right now I am just making manual loops for each potential nested array. (Which is a lot of extra copy paste code)

for(var i = 0; i < searchableArray.length; ++i) 
{
    if(searchableArray[i].id == 6) //6 would actually be a variable, just doing a manual example
    {
        if(searchableArray[i].nestedElements.length > 0)
        {
            for(var j = 0; j < searchableArray[i].nestedElements.length; ++j) 
            {
                if(searchableArray[i].nestedElements[j].id == '7')
                {
                    if(searchableArray[i].nestedElements[j].type == 'other-type')
                    {
                        searchableArray[i].nestedElements[j].dosomething = 'do this to something in the object';
                    }
                    else if(searchableArray[i].nestedElements[j].type == 'another-type')
                    {
                        searchableArray[i].nestedElements[j].dosomething = 'do this other thing to the object';
                    }
                }
            }
        }
    }
}

This would get very huge with nested loops for everything, so is there any easier way to do it?

Thanks!

4 Answers

We try not to reinvent too much these days and I'd suggest object-scan for this. It's pretty powerful once you wrap your head around it. Here is how you'd use it:

// const objectScan = require('object-scan');

const modify = (id, task, data) => objectScan(['**.id'], {
  abort: true,
  rtn: 'bool',
  filterFn: ({ value, parent }) => {
    if (value === id) {
      task(parent);
      return true;
    }
    return false;
  }
})(data);

const searchableArray = [{ id: 3, type: 'some-type', text: 'text', nestedElements: [{ id: 4, type: 'some-type', text: 'other text', nestedElements: [] }, { id: 5, type: 'another-type', text: 'more text', nestedElements: [] }] }, { id: 6, type: 'other-type', text: 'text', nestedElements: [{ id: 7, type: 'other-type', text: 'other text', nestedElements: [] }, { id: 8, type: 'another-type', text: 'more text', nestedElements: [] }] }, { id: 9, type: 'another-type', text: 'text', nestedElements: [{ id: 10, type: 'another-type', text: 'other text', nestedElements: [] }, { id: 11, type: 'another-type', text: 'more text', nestedElements: [] }] }];

console.log(modify(7, (obj) => {
  if (obj.type === 'other-type') {
    obj.dosomething = 'do this to something in the object';
  } else if (obj.type === 'another-type') {
    obj.dosomething = 'do this other thing to the object';
  }
}, searchableArray)); // true iff executed
// => true

console.log(searchableArray);
// => [ { id: 3, type: 'some-type', text: 'text', nestedElements: [ { id: 4, type: 'some-type', text: 'other text', nestedElements: [] }, { id: 5, type: 'another-type', text: 'more text', nestedElements: [] } ] }, { id: 6, type: 'other-type', text: 'text', nestedElements: [ { id: 7, type: 'other-type', text: 'other text', nestedElements: [], dosomething: 'do this to something in the object' }, { id: 8, type: 'another-type', text: 'more text', nestedElements: [] } ] }, { id: 9, type: 'another-type', text: 'text', nestedElements: [ { id: 10, type: 'another-type', text: 'other text', nestedElements: [] }, { id: 11, type: 'another-type', text: 'more text', nestedElements: [] } ] } ]
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/[email protected]"></script>

Disclaimer: I'm the author of object-scan

Answered by vincent on January 5, 2022

This is what you want:

const searchableArray = [];

searchableArray.push({ id: 3, type: 'some-type', text: 'text', nestedElements: [{ id: 4, type: 'some-type', text: 'other text', nestedElements: [] }, { id: 5, type: 'another-type', text: 'more text', nestedElements: [] }] })
searchableArray.push({ id: 6, type: 'other-type', text: 'text', nestedElements: [{ id: 7, type: 'other-type', text: 'other text', nestedElements: [] }, { id: 8, type: 'another-type', text: 'more text', nestedElements: [] }] })
searchableArray.push({ id: 9, type: 'another-type', text: 'text', nestedElements: [{ id: 10, type: 'another-type', text: 'other text', nestedElements: [] }, { id: 11, type: 'another-type', text: 'more text', nestedElements: [] }] });

const find = (id, cb) => {
    const ar = searchableArray.slice(0);
    for (var i = 0; i < ar.length; i++) {
        if (ar[i].id === id) {
            return cb(ar[i]);
        }
        if (ar[i].nestedElements.length) {
            ar.push(...ar[i].nestedElements);
        }
    }

}

find(7, (o) => {
    if (o.type == 'other-type') {
        o.dosomething = 'do this to something in the object';
    } else if (o.type == 'another-type') {
        o.dosomething = 'do this other thing to the object';
    }
});

console.log(JSON.stringify(searchableArray));

Answered by 8HoLoN on January 5, 2022

You can simplify your structure by some code refactoring:

  1. Nested if can be avoided by && operator.
  2. You can avoid the inner for loop and use find instead.

let searchableArray = [];

searchableArray.push({id: 3, type: 'some-type', text: 'text', nestedElements: [{id: 4, type: 'some-type', text: 'other text', nestedElements: []}, {id: 5, type: 'another-type',  text: 'more text', nestedElements: []}]})
searchableArray.push({id: 6, type: 'other-type', text: 'text', nestedElements: [{id: 7, type: 'other-type', text: 'other text', nestedElements: []}, {id: 8, type: 'another-type',  text: 'more text', nestedElements: []}]})
searchableArray.push({id: 9, type: 'another-type', text: 'text', nestedElements: [{id: 10, type: 'another-type', text: 'other text', nestedElements: []}, {id: 11, type: 'another-type',  text: 'more text', nestedElements: []}]})


searchableArray.forEach(item => {
    if(item.id === 6 && item.nestedElements.length > 0 && item.nestedElements.find(item => item.id === 7 && item.type === "other-type")){
        item.nestedElements.find(item => item.id === 7 && item.type === "other-type").dosomething = 'do this to something in the object';       
    } else if (item.id === 6 && item.nestedElements.length > 0 && item.nestedElements.find(item => item.id === 7 && item.type === "another-type")){
        item.nestedElements.find(item => item.id === 7 && item.type === "another-type").dosomething = 'do this other thing to the object';
    }   
});

console.log(searchableArray);

Answered by Ravi Kukreja on January 5, 2022

Example:

searchableArray.forEach(function(el){
                    if (el.hasOwnProperty("nestedElements") && el.nestedElements instanceof Array){
                        el.nestedElements.forEach(function(el1){
                           if (el1.id===7){
                               
                           }
                        });
                    }
            });

Answered by toto on January 5, 2022

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP