Recursively iterate over a nested object to change a key value on all occurrences

2.2k Views Asked by At

There is an input structure with rules nested inside other rules. In the rules array, wherever there is a 'data' property, its value has to be changed to "foo"

Sample input objects:

  1. {condition: 'and', rules: [{data: '123'}]}

  2. {condition: 'or', rules: [ { data: '123'}, {condition: 'and', rules: [{data:'123'},{ data:'456'}] }]

Am recursively calling a function to iterate over and if the item has data property, changing its value

My function:

function iterateRules(input) {
    input.rules.map(function(item) {
      if(_.has(item, "rules")){
        this.iterateRules(item); //bug-needs extra check like accepted answer 
      } else if(_.has(item, “data”)){
         return item.data = “foo”;
      }
    }, this);
   return input;
 }
2

There are 2 best solutions below

1
On BEST ANSWER

There is a potential bug in the code you mentioned.

  1. In the recursive call to iterateRules you are passing input instead of item
  2. You also need to check if the input has rules property

Try this -

function iterateRules(input) {
  if(_.has(input, "rules")) {
    input.rules.map(function(item) { 
      if(_.has(item, "rules")){
        this.iterateRules(item);
      } else if (_.has(item, "data")) {
          return item.data = "foo";
      }
    }, this);
    console.log(input);
  }
}
0
On

There is a recursive approach to achieve this:

const input = {condition: "and", rules: [ { data: "123"}, {condition: "and", rules: [{data:"456"}, {condition: "and", rules: [{value: "456"}]} ] } ]}

function test (obj) {
 if(!Object.keys(obj).includes('rules')) return;
 obj.rules.forEach(x => x.data ? x.data = 'foo' : x);
 return test(obj.rules.find(x => !Object.keys(x).includes('data')));
}

test(input)

console.log(input);

note: this function changes input obj.