36

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?

dpkwhan
  • 371
  • 1
  • 3
  • 4

6 Answers6

59

You can achieve it with a form of destructuring:

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

However, at the time of writing this answer, the spread (...) syntax is still at the ECMAScript proposal stage (stage 3), so it may not be universally available in practice. You may use it with a "transpilation" layer such as Babel.

Armen Michaeli
  • 8,625
  • 8
  • 58
  • 95
madox2
  • 49,493
  • 17
  • 99
  • 99
  • currently stage 4, according to the first link – YakovL Sep 20 '19 at 10:31
  • 4
    Please note that this will also create an `_id` variable. – RedGuy11 Apr 12 '21 at 20:00
  • @RedGuy11 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. – Galen Howlett Jan 10 '23 at 17:23
  • 1
    I was pointing out that the `_id` variable would be created – RedGuy11 Jan 11 '23 at 18:51
6

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);
Pranav C Balan
  • 113,687
  • 23
  • 165
  • 188
  • 2
    I wonder how this can be faster than the assignment that the OP makes. – trincot Nov 12 '16 at 12:33
  • 1
    @trincot - 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. – adeneo Nov 12 '16 at 14:37
  • Indeed, the `Object.assign` is of course superfluous in the OP's code. – trincot Nov 12 '16 at 14:53
  • @trincot : `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? – Pranav C Balan Nov 13 '16 at 13:31
  • @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. – trincot Nov 13 '16 at 13:37
  • @trincot `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... – Pranav C Balan Nov 13 '16 at 13:42
  • I agree that is enough (it is what [I answered](http://stackoverflow.com/a/40564052/5459839)), and I agree his expectations are not clear. We can only wait for the OP's return from his absence :) – trincot Nov 13 '16 at 13:51
4

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};
trincot
  • 317,000
  • 35
  • 244
  • 286
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)
adeneo
  • 312,895
  • 29
  • 395
  • 388
  • 1
    How can a loop be faster than the direct assignment that the OP performs? – trincot Nov 12 '16 at 12:34
  • @trincot - `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 -> **https://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. – adeneo Nov 12 '16 at 14:31
  • Indeed, the `Object.assign` is of course superfluous in the OP's code. – trincot Nov 12 '16 at 14:54
  • @trincot - 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 ? – adeneo Nov 12 '16 at 15:07
  • 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. – trincot Nov 12 '16 at 15:21
2

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: "john123@example.com",
  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":"john123@example.com","created_at":"2001-01-01T01:30:18Z"}
s-hunter
  • 24,172
  • 16
  • 88
  • 130
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'])
);
SimoAmi
  • 1,696
  • 14
  • 13