DEV Community

Cover image for Manipulating Keys in an Object Using Recursion
Edward Ellington
Edward Ellington

Posted on

Manipulating Keys in an Object Using Recursion

It is no secret that Javascript was designed for object-oriented programming. Objects have the ability to store string, numeric and boolean values as well as functions (methods) in an easy to read and interpret way. Because understanding objects and how they work, or can be accessed, is a necessity in Javascript I would like to share an example of how you can manipulate an objects key to your will with the power of recursion!

Recursively Replacing a Key in an Object

In the event that you may need to replace a key in an object, you can use a short little function that cleverly uses recursion to replace the key in an object.

var replaceKeysInObj = (obj, key, newKey) => {
     let newObj = {};
     for (var ogKey in obj){
       if(ogKey === key) {
         newObj[newKey] = obj[ogKey]
    } else if (typeof obj[ogKey] === 'object') {
      newObj[ogKey] = replaceKeysInObj(obj[ogKey], key, newKey);
    } else {
      newObj[ogKey] = obj[ogKey]
    }
  }
  return newObj
};
Enter fullscreen mode Exit fullscreen mode

Here is a breakdown of the above function

Note - The three parameters are going to be the original object(obj), the key we are going to change(key), and the new key we are going to use to replace the old key(newKey).

First: we create a new variable(newObj)and set it to an object literal({}). We will use this new object literal for creating our new and improved key in the object.

Second:

for (var ogKey in obj){
       if(ogKey === key) {
         newObj[newKey] = obj[ogKey]
Enter fullscreen mode Exit fullscreen mode

We use a for-in loop to see if the key in the object(ogKey) matches the key we are looking to replace(key). If it does then we are setting the new object key to the old object key.

Third: time to use recursion

else if (typeof obj[ogKey] === 'object') {
      newObj[ogKey] = replaceKeysInObj(obj[ogKey], key, newKey);
Enter fullscreen mode Exit fullscreen mode

If the type of value for the object's original key is an object, then we set the value equal to the recursive case so it will also look inside of that object which is not just helpful... it's AWESOME.

Forth:

else {
      newObj[ogKey] = obj[ogKey]
    }
Enter fullscreen mode Exit fullscreen mode

The final else, if the first two conditions aren't met, will set the new object's key/value equal to the original object's key/value. This will allow the rest of the object's keys that do not match the "newKey" to stay the same after the recursive calls.

Fifth & Final:

return newObj
};
Enter fullscreen mode Exit fullscreen mode

We return the new object that has our updated key inside.

There is always another way to skin a cat (lookup Object.assign) but this way allows you to check through nested objects with the power of recursion!

Top comments (6)

Collapse
 
nicholaspedroso profile image
Nicholas Pedroso

Nice article man! Based on your solution I came up with a different one using reduce

const replaceKey = (obj, oldKey, newKey) => Object.entries(obj).reduce((acc, [key, value]) => {
    if (key === oldKey) { acc[newKey] = value; return acc } 
    if (typeof value === 'object') { acc[key] = replaceKey(value, oldKey, newKey); return acc }
    acc[key] = value; return acc
}, {})
Enter fullscreen mode Exit fullscreen mode
Collapse
 
ridhwaans profile image
ridhwaans • Edited

I updated your answer slightly to include objects whose value type is a list of objects

const replaceKey = (obj, oldKey, newKey) => Object.entries(obj).reduce((acc, [key, value]) => {
    if (key === oldKey) { acc[newKey] = value; return acc } 
    if (typeof value === 'object') {
      if (value.length) { acc[key] = value.map(cur_val => { return replaceKey(cur_val, oldKey, newKey) }); return acc }
      else { acc[key] = replaceKey(value, oldKey, newKey); return acc }
    }
    acc[key] = value; return acc
}, {})
Enter fullscreen mode Exit fullscreen mode
Collapse
 
ridhwaans profile image
ridhwaans

this doesnt work as expected if the value type is a list of objects
example

recommended: [
    { id: 1, external_id: 1, categories: [Array] },
    { id: 1, external_id: 1, categories: [Array] }
  ],
Enter fullscreen mode Exit fullscreen mode
recommended: {
    '0': { id: 1, changed_key: 1, categories: [Object] },
    '1': { id: 1, changed_key: 1, categories: [Object] }
  },
Enter fullscreen mode Exit fullscreen mode
Collapse
 
aminnairi profile image
Amin • Edited

Hi there and thanks for this interesting article!

Here is an alternative using a reducer function. I also took the liberty of renaming the function replaceAll since it will replace all properties, even the nested ones.

function replaceAll(oldName, newName, target) {
    function reducer(newTarget, [currentName, currentValue]) {
        if (typeof currentValue === "object") {
            return {
                ...newTarget,
                [currentName]: replaceAll(oldName, newName, currentValue)
            };
        }

        if (currentName === oldName) {
            return {
                ...newTarget,
                [newName]: currentValue
            };
        }

        return {
            ...newTarget,
            [currentName]: currentValue
        };
    }

    return Object.entries(target).reduce(reducer, {});
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
eellin6 profile image
Edward Ellington

Wow... Thank you very much!

Collapse
 
juliathea profile image
Julia Thea

Thank you for your article! It helped me to solve my problem. However there is an issue with your code. The else if statement doesn't account for the cases when the current key matches the key to be replaced AND the value of that key is an object.
I used your idea in my solution and came up with the following:

var replaceKeysInObj = function(obj, oldKey, newKey) {

for (let key in obj) {
if (typeof(obj[key]) === 'object') {
obj[key] = replaceKeysInObj(obj[key], oldKey, newKey)
}
if (key === oldKey) {
obj[newKey] = obj[key];
delete obj[key];
}
}

return obj;
};

Thank you for the idea again, I was so stuck!