Traversing recursively through an array and modifying values of object properties in JavaScript

Tags: , , , ,



The post may seem lengthy but it’s quite easy to follow, if not, I will add more details

I have criteria array which that looks like :

   let criteria = [
        "and",
        {
          "Collection": "persons",
          "Property": "phone",
          "operator": "eq",
          "operatorValue": "$p:phone"  
        },
        {
          "Collection": "persondetails",
          "Property": "country",
          "operator": "eq",
          "operatorValue": "$p:country" 
        },
        ["or",
        {
          "Collection": "persons",
          "Property": "city",
          "operator": "eq",
          "operatorValue": "$p:city"  
        }]
      ]

Characteristics of criteria:

  1. It could have nested arrays.
  2. First item array (or nested array) is always going to be either “and” or “or”
  3. Second item onwards in array, item could be either an object with this specific structure

{ "Collection": "persons", "Property": "phone", "operator": "eq", "operatorValue": "$p:phone" }

or it could be an array like:

["or", { "Collection": "persons", "Property": "city", "operator": "eq", "operatorValue": "$p:city" }]

  1. Object is never going to be nested object

There is also a parameters object:

let parameters = {phone:"23138213", "country": "Russia", "city":"york"}

The aim is to recursively traverse through all the operatorValue properties in the criteria array and if you encounter value such as $p:phone, it is to be replaced by whatever parameters["phone"] evaluate to.

EXPECTED OUTPUT:

[
    "and",
    {
      "Collection": "persons",
      "Property": "phone",
      "operator": "eq",
      "operatorValue": "23138213"  
    },
    {
      "Collection": "persondetails",
      "Property": "country",
      "operator": "eq",
      "operatorValue": "Russia" 
    },
    ["or",
    {
      "Collection": "persons",
      "Property": "city",
      "operator": "eq",
      "operatorValue": "york"  
    }]
  ]

I was able to recursively traverse through the array. The only problem is that I can’t figure out how to modify the original criteria variable.

REPL

Please see the line 43 in repl. item[1]=parameters[item[1].split('$p:')[1]] I understand why it won’t modify the criteria, its because item over here is a different variable in different scope altogether.

Failed Attempt :

  function traverse(obj,parameters){
  
    obj.forEach((item,index)=>{
      
      
     if( typeof item == 'string' ){
       //do nothing
     }
     else if( !(item instanceof Array)){
         
       Object.entries(item).forEach((item,index)=>{
         
         if( item[1] instanceof Array){ 
                      
           traverse(item,parameters);
         }else{
           if(item[1].startsWith('$p:')){
               item[1]=parameters[item[1].split('$p:')[1]] //values dont get replaced for obvious reason
               console.log(item[1])
           } 
         }
       })        
     }
     else if( item  instanceof Array){           
           traverse(item,parameters);
      }  
    })
  }

  traverse(criteria,parameters)
  console.log(criteria)

How do I go about solving this problem?

Answer

You could simplify your function. You don’t need to loop through the entries of the object. You also don’t need to split the operationValue. That mapping key of parameters is present in the Property key.

  • Loop through each item in the array and check if the item is an Array.
  • If yes, recursively call traverse on the item.
  • If it is an object, update its operatorValue property with parameters[val.Property]

function traverse(arr, parameters) {
  for (const item of arr) {
    if (Array.isArray(item))
        traverse(item, parameters)
    else if (typeof item === 'object')
        item.operatorValue = parameters[item.Property]
  }
  return arr
}

let criteria=["and",{Collection:"persons",Property:"phone",operator:"eq",operatorValue:"$p:phone"},{Collection:"persondetails",Property:"country",operator:"eq",operatorValue:"$p:country"},["or",{Collection:"persons",Property:"city",operator:"eq",operatorValue:"$p:city"}]],
    parameters = {phone:"23138213", "country": "Russia", "city":"york"};

console.log(traverse(criteria, parameters))


Source: stackoverflow