What is the most efficient way to copy some properties from an object in JavaScript?
Asked Answered
D

7

40

Say, I have an object:

const user = {_id: 1234, firstName: 'John', lastName: 'Smith'}

I want to create another object without the _id key:

const newUser = {firstName: 'John', lastName: 'Smith'}

I am using this:

const newUser = Object.assign({}, {firstName: user.firstName, lastName: user.lastName})

Is there a better way to do this?

Declaim answered 12/11, 2016 at 12:3 Comment(0)
T
64

You can achieve it with a form of destructuring:

const user = { _id: 1234, firstName: 'John', lastName: 'Smith' };
const { _id, ...newUser } = user;
console.debug(newUser); 

Browser support:

The spread (...) syntax was introduced with ES2018, and most browsers even supported it before ES2018 was finalized. So as of 2023, you can rely on browser support unless you need to support very old browsers. Alternatively, you may use it with a "transpilation" layer such as Babel.

Truly answered 12/11, 2016 at 12:9 Comment(4)
currently stage 4, according to the first linkWrennie
Please note that this will also create an _id variable.Albaalbacete
@Albaalbacete that's not true, the destructure pulls the _id value out of user and assigns it to a new variable _id while adding the rest of the properties to a new newUser object. The downside with this approach is that if the user object is modified over time, you may have to modify the destructuring statement to define what properties are omitted. Basically there is a decision of whether you would rather pull the specific name values or remove the id value if the user object changes over time.Acculturize
I was pointing out that the _id variable would be createdAlbaalbacete
C
7

Do it with Array#reduce method with Object.keys method.

const user = {
  _id: 1234,
  fistName: 'John',
  lastName: 'Smith'
};

var res = Object.keys(user).reduce(function(obj, k) {
  if (k != '_id') obj[k] = user[k];
  return obj;
}, {});

console.log(res);
Carsoncarstensz answered 12/11, 2016 at 12:7 Comment(7)
I wonder how this can be faster than the assignment that the OP makes.Junette
@Junette - why not read the question, it says "I want to create another object without the _id key", and then the OP shows the object that is the expected result, and goes on to say that he's using Object.assign, which is not direct assigment.Question
Indeed, the Object.assign is of course superfluous in the OP's code.Junette
@Junette : Object.assign({}, {firstName: user.firstName, lastName: user.lastName}) is same as simple assigning {firstName: user.firstName, lastName: user.lastName}.. I think he want to copy all other property except _id.... in case he don't knows what are the property which have?Carsoncarstensz
@PranavCBalan, I understand that it is nice to have a generic solution, but the OP is not clear on that, as he seems to have a working solution that has hard-coded property names. For your first statement: I agree the outcome is the same, but with Object.assign two new objects are created (cf. the two object literals) instead of one, and so also the shallow copy is taken twice, not once.Junette
@Junette const newUser = {firstName: user.firstName, lastName: user.lastName} is enough .... what he is expecting as result is not at all clear.... I think he need a generic solution for doing that...Carsoncarstensz
I agree that is enough (it is what I answered), and I agree his expectations are not clear. We can only wait for the OP's return from his absence :)Junette
J
6

You are taking a shallow copy twice: once with the object literal, and again with Object.assign. So just use the first of the two:

const newUser = {firstName: user.firstName, lastName: user.lastName};
Junette answered 12/11, 2016 at 14:53 Comment(0)
M
3

Go through the object keys, put the wanted property keys in an array and use the Array.prototype.includes() to copy only these into the new object.

const account = {
  id: 123456,
  firstname: "John",
  lastname: "Doe",
  login: "john123",
  site_admin: false,
  blog: "https://opensource.dancingbear/",
  email: "[email protected]",
  bio: "John ❤️ Open Source",
  created_at: "2001-01-01T01:30:18Z",
  updated_at: "2020-02-16T21:09:14Z"
};

function selectSomeProperties(account) {
return Object.keys(account).reduce(function(obj, k) {
    if (["id", "email", "created_at"].includes(k)) {
        obj[k] = account[k];
    }
    return obj;
  }, {});
}
const selectedProperties = selectSomeProperties(account);
console.log(JSON.stringify(selectedProperties))

The result:

{"id":123456,"email":"[email protected]","created_at":"2001-01-01T01:30:18Z"}
Metamer answered 17/2, 2020 at 7:14 Comment(0)
Q
2

The most efficient would most likely be a regular loop

const user = {_id: 1234, fistName: 'John', lastName: 'Smith'};
let   obj  = {}, key;

for (key in user) {
  if ( key !== '_id' ) obj[key] = user[key];
}

console.log(obj)
Question answered 12/11, 2016 at 12:13 Comment(5)
How can a loop be faster than the direct assignment that the OP performs?Junette
@Junette - Object.assign is not "direct assigment", it's an internal method that iterates, does some checks, and creates a new object, and it's not very efficient -> jsperf.com/object-copy-efficiency-s. Of course, if the op can use direct assigment, and can reference the keys directly, that's faster, but this just leaves out the _id property, like the other answers, and assumes one doesn't always want to use all the keys.Question
Indeed, the Object.assign is of course superfluous in the OP's code.Junette
@Junette - of course, as the properties are primitives, the assign call isn't needed at all, the OP is passing the object he wants to Object.assign and gets a copy of the exact same object, I just assumed the point was to leave out the _id property when creating the new object, not reference the other properties directly ?Question
The question is indeed ambiguous. The OP writes I am currently using this, which would not be a possibility if the solution had to work for objects with other properties in his code as well. Not sure now.Junette
S
1

This tiny function will select specific keys to either copy or exclude from copying. exclude take precedence:

function copy(obj, include=[], exclude=[]) {

  return Object.keys(obj).reduce((target, k) => {

    if (exclude.length) {
      if (exclude.indexOf(k) < 0) target[k] = obj[k];
    } else if (include.indexOf(k) > -1) target[k] = obj[k];
    return target;
  }, {});
}

// let's test it
const user = {
  _id: 1234,
  firstName: 'John',
  lastName: 'Smith'
};

// both will return the same result but have different uses.
console.log(
  'include only firstName and lastName:\n', 
  copy(user, ['firstName', 'lastName'])
);
console.log(
  'exclude _id:\n',
  copy(user, null, ['_id'])
);
Sneak answered 6/4, 2018 at 2:39 Comment(0)
S
0

If by 'better way' you're just looking for a more elegant-looking way, the way I usually do it is:

const { firstName, lastName } = user;
const newUser = { firstName, lastName };

Note that it "contaminates" the scope with two more variables, but it's short and at least you're specifying the variables you need instead of the ones you want to exclude.

Sheer answered 30/1 at 15:39 Comment(2)
Having two extra variables won't impact your code performance really. The processor can handle that, but the developers experience will be bumped by a lot being that descriptive I'd say, so I like this!Lawyer
yeah there is probably no performance issue, I removed that remark. the only thing to have in mind still is that it introduces those variables to the current scope, which can be a little confusingSheer

© 2022 - 2024 — McMap. All rights reserved.