30 Seconds of Code is a brilliant collection of JavaScript snippets, digestible in ≤ 30 seconds. Anyone looking to master JavaScript should go through the entire thing.

The list didn’t contain a function to rename multiple object keys, however, so I created a pull request that thankfully got merged!

Here’s the official entry: https://30secondsofcode.org/object#renamekeys

I’ve previously written on renaming object keys, but we only changed one key at a time.

Then Adam Rowe kindly commented, asking how we might rename multiple object keys. I replied with this code sample after some thought and research.

renameKeys = (keysMap, obj) =>
  Object.keys(obj).reduce(
    (acc, key) => ({
      ...acc,
      ...{ [keysMap[key] || key]: obj[key] }
    }),
    {}
  );

This was inspired by Ramda Adjunct’s renameKeys function.

  • keysMap contains key/value pairs of your old/new object keys.
  • obj is the object to be changed.

You might use it like this:

keysMap = {
  name: 'firstName',
  job: 'passion'
};

obj = {
  name: 'Bobo',
  job: 'Front-End Master'
};

renameKeys(keysMap, obj);
// { firstName: 'Bobo', passion: 'Front-End Master' }

Let’s step through it! We can write an expanded, debugger-friendly version of this function:

renameKeys = (keysMap, obj) => {
  debugger;

  return Object.keys(obj).reduce((acc, key) => {
    debugger;

    const renamedObject = {
      [keysMap[key] || key]: obj[key]
    };

    debugger;

    return {
      ...acc,
      ...renamedObject
    };
  }, {});
};

And we’ll use it like this:

renameKeys(
  {
    name: 'firstName',
    job: 'passion'
  },
  {
    name: 'Bobo',
    job: 'Front-End Master'
  }
);

Pausing on line 2, we see that keysMap and obj have been properly assigned.

Here’s where the fun begins. Move to the next debugger.

Inspect our local variables on line 7:

  • acc: {} because that’s Array.reduce()’s initial value (line 19).
  • key: “name” because it’s the first key from Object.keys(obj).
  • renamedObject: undefined

Also notice that we can access keysMap and obj from the parent function’s scope.

Guess what renamedObject will be. Like in my aforementioned post, we’re using computed property names to dynamically assign renamedObject's key.

If keysMap[key] exists, use it. Otherwise, use the original object key. In this case, keysMap[key] is “firstName”.

That’s renamedObject's key, what about its corresponding value?

It’s obj[key]: "Bobo". Hit the next debugger and check it out.

renamedObject is now { firstName: “Bobo” }.

Now using the spread operator, we’ll merge acc and renamedObject. Remember that acc is currently .reduce's initial value: an empty object.

So merging acc and renamedObject just results in a clone of renamedObject.

Since we’re returning this object, however, it becomes acc in .reduce’s next iteration. Move to the next debugger to see this.

We’re inside .reduce's again, because there’s one more key to process. We see that acc is now { firstName: "Bobo" }.

The same process runs again, and renamedObject is properly created.

This time, merging acc and renamedObject actually makes a difference.

Run past this debugger to return that object, and you’re done!

Here’s the final output:

Have fun renaming all the keys, until next time!