What's the shortest way to copy only selected attributes in JavaScript?

249 Views Asked by At

Given the following two objects:

let a = { foo: 'bar' };

let b = {
  one: 1,
  two: 2,
  three: 3,
  four: 4,
  five: 5,
  six: 6,
  seven: 7
};

We can assume that all values within these objects are primitives. The goal is to copy some attributes from object b to object a, but not all.

Example:

a.one = b.one;
a.two = b.two;
// Do not copy item three
a.four = b.four;
a.five = b.five;
a.six = b.six;
// Do not copy item seven

In this example we have omitted attributes 3 and 7 but we could also omit any other attribute or even more than just two items. As you can see this solution can be quite cumbersome if we are dealing with many attributes.

Question: What's the shortest , cleanest and most simple solution to achieve something like this with modern JavaScript in 2021?

Note: For the solution you can assume there is an array of strings given. Each string represents a key of a value that should be copied. So, in the example above you can assume const keys = ['one', 'two', 'four', 'five', 'six']; being given.

8

There are 8 best solutions below

2
Gabriel Lupu On BEST ANSWER

I would use a forEach on the array of given keys to copy what I need:

const a = {
  foo: 'bar'
};

const b = {
  one: 1,
  two: 2,
  three: 3,
  four: 4,
  five: 5,
  six: 6,
  seven: 7
};

const doCopy = ['two', 'five'];

doCopy.forEach(el => a[el] = b[el]);

console.log(a);

0
Timo Ernst On

This would be my solution. Even if it works I don't like having to import a dependency. Wondering if there is a better built-in solution to modern JavaScript.

util.js

export function copySelected(a, b, attrs) {
  for (let i=0; i < attrs.length; i+=1) {
    const attr = attrs[i];
    a[attr] = b[attr];
  }
  return a;
}

Usage:

import { copySelected } from './util';

let a = { foo: 'bar' };

let b = {
  one: 1,
  two: 2,
  three: 3,
  four: 4,
  five: 5,
  six: 6,
  seven: 7
};

copySelected(a, b, ['one', 'two', 'four', 'five', 'six']);
0
Scott Marcus On

See comments:

const keys = ['one', 'two', 'four', 'five', 'six'];

let a = { foo: 'bar' };

let b = {
  one: 1,
  two: 2,
  three: 3,
  four: 4,
  five: 5,
  six: 6,
  seven: 7
};

// Loop over the array
keys.forEach(function(key){
  // If the b object contains the key specified in the array
  if(b[key]){
    a[key] = b[key];  // copy that key to a
  }
});

console.log(a);

1
Gary On

Same idea as @Timo's answer, but with a map function and spread operator.

function copySelected(a, b, ...attrs) {
  attrs.map(x => a[x] = b[x]);
  return a;
}

let a = { foo: 'bar' };

let b = {
  one: 1,
  two: 2,
  three: 3,
  four: 4,
  five: 5,
  six: 6,
  seven: 7
};

console.log( copySelected(a, b, 'one', 'two', 'four', 'five', 'six'));

0
Parham Heidari On

Here is an example with ECMAScript 2021:

const b = {
  one: 1,
  two: 2,
  three: 3,
  four: 4,
  five: 5,
  six: 6,
  seven: 7
};

const copyFromThis = ['one','two']

const temp = Object.fromEntries(Object.entries(b).filter(([key , value])=>copyFromThis.includes(key) ))

console.log(temp)

you can customize it as you want!

0
Nick On

You could use Object.assign to add the filtered (using the keys array to filter key/value pairs) entries from b to a:

const a = {
  foo: 'bar'
};

const b = {
  one: 1,
  two: 2,
  three: 3,
  four: 4,
  five: 5,
  six: 6,
  seven: 7
};

const keys = ['one', 'two', 'four', 'five', 'six'];

const res = Object
  .assign(a, Object.fromEntries(Object.entries(b)
    .filter(([k, v]) => keys.includes(k))
  ))
  
console.log(res)

0
Nina Scholz On

You could assign the wanted properties.

a) without an array for assigning

let a = { foo: 'bar' },
    b = { one: 1, two: 2, three: 3, four: 4, five: 5, six: 6, seven: 7 },
    keys = ['one', 'two', 'four', 'five', 'six'];

keys.forEach(k => Object.assign(a, { [k]: b[k] }));

console.log(a);

b) with an array for assigning

let a = { foo: 'bar' },
    b = { one: 1, two: 2, three: 3, four: 4, five: 5, six: 6, seven: 7 },
    keys = ['one', 'two', 'four', 'five', 'six'];

Object.assign(a, ...keys.map(k => ({ [k]: b[k] })));

console.log(a);

0
Bergi On

The simplest (and shortest) is probably still a loop:

const res = {};
for (const key of keys) res[key] = source[key];

You can also write this using reduce:

const res = keys.reduce((target, key) => { target[key] = source[key]; return target }, {});
const res = keys.reduce((target, key) => Object.assign(target, {[key]: source[key]}), {});

Or, rather elegant, using Object.fromEntries:

const res = Object.fromEntries(keys.map(key => [key, source[key]]));

(this does however not support assigning to an existing object).