Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Replace all object values recursively

Tags:

javascript

I am using angular 4 for one of my projects and i am in a situation where i have to replace all the {data} with {{data}}. ie if i have an object like this

{
    key: {
             key1: {
                       key2: "This is data {someData}",
                       key3: "This is data2 {someData2}"
                   }
         },
    key2: "This is data3 {someData3}"
}

the result should be

{
    key: {
             key1: {
                       key2: "This is data {{someData}}",
                       key3: "This is data2 {{someData2}}"
                   }
         },
    key2: "This is data3 {{someData3}}"
}

I am not so comfortable with recursion as on how it works . So i am not able to show anything that i have tried. Please help. Any help would be appreciated.

like image 593
Vikhyath Maiya Avatar asked Apr 22 '18 04:04

Vikhyath Maiya


2 Answers

Hopefully you can try something like this.

var orgObj = {
  key: {
    key1: {
      key2: "This is data {someData}",
      key3: "This is data2 {someData2}"
    }
  },
  key2: "This is data3 {someData3}"
}

function changeValue(obj) {
  if (typeof obj === 'object') {
    // iterating over the object using for..in
    for (var keys in obj) {
      //checking if the current value is an object itself
      if (typeof obj[keys] === 'object') {
        // if so then again calling the same function
        changeValue(obj[keys])
      } else {
        // else getting the value and replacing single { with {{ and so on
        let keyValue = obj[keys].replace('{', '{{').replace('}', '}}');
        obj[keys] = keyValue;
      }
    }
  }
  return obj;
}
console.log(changeValue(orgObj))
like image 129
brk Avatar answered Nov 09 '22 22:11

brk


Answer by @brk is good but it is mutates the input object. If someone is working with a library like React where it is important to not mutate props, then we will have to do some additional things. We can either create new reference for all the objects that we find during recursive traversal. For this, we have to take care of null values since typeof null is also object

//typeof null is also object. But we cannot destructure it. So check that object is not null
const isValidObject = (obj) => typeof obj === 'object' && obj !== null

function changeValue(objFromProp) {
    let obj = objFromProp;

    if (isValidObject(objFromProp)) {
        //desctructure the object to create new reference
        obj = { ...objFromProp }
        // iterating over the object using for..in
        for (var keys in obj) {
            //checking if the current value is an object itself
            if (isValidObject(obj[keys])) {
                // if so then again calling the same function
                obj[keys] = changeValue(obj[keys])
            } else {
                // else getting the value and replacing single { with {{ and so on
                let keyValue = obj[keys].replace('{', '{{').replace('}', '}}');
                obj[keys] = keyValue;
            }
        }
    }
    return obj;
}

Or if we are replacing values conditionally and if we want to preserve object's reference if none of the values in an object is changed, then we can use another library like immer. Immer will change reference of only those objects whose at-least one field is changed. This will be a useful optimization. For this method, we should use version of changeValue provided by @brk. Immer will identify mutations and change necessary references.

import { produce } from 'immer';
const nextState = produce(objFromProp, draftState => {
    changeValue(draftState)
})
like image 23
Omkar Rajam Avatar answered Nov 09 '22 22:11

Omkar Rajam