How can I clone a JavaScript object except for one key?
Asked Answered
R

25

691

I have a flat JS object:

{a: 1, b: 2, c: 3, ..., z:26}

I want to clone the object except for one element:

{a: 1, c: 3, ..., z:26}

What's the easiest way to do this (preferring to use es6/7 if possible)?

Riga answered 9/1, 2016 at 21:3 Comment(2)
Very careful with answers on this question, almost all of them at this time don't clone Deeply the rest of object. They are doing just a Shallow clone. All descendants properties of children props are passed by reference and can be changed through other variables. Please see #122602. As of 2023, for the given example use structuredClone fallbacking to JSON.parse(JSON.stringify methodTorgerson
^^^ this is an excellent caveatRiga
M
929

There is a Destructuring assignment syntax in JavaScript that can be used

let obj = {a: 1, b: 2, c: 3, z:26};
let {b, ...rest} = obj;

// skips the "Unused variable" warning
let {b: _, ...rest} = obj;

// removes property based on the dynamic key
const dynamicKey = "b";
let {[dynamicKey]: _, ...rest} = obj;

Modern browsers already support it out of the box. See: JavaScript operator: Destructuring assignment: Rest in objects

For old browser versions there is an option to use Babel to support destructuring assignment. It will be transpiled into:

"use strict";

function _objectWithoutProperties(obj, keys) {
  var target = {};
  for (var i in obj) {
    if (keys.indexOf(i) >= 0) continue;
    if (!Object.prototype.hasOwnProperty.call(obj, i)) continue;
    target[i] = obj[i];
  }
  return target;
}

var x = { a: 1, b: 2, c: 3, z: 26 };
var b = x.b;

var y = _objectWithoutProperties(x, ["b"]);
Mendelson answered 10/1, 2016 at 19:54 Comment(15)
If you lint your code for unused variables, this will result in an "Unused variable 'b'." warning though.Hushaby
how would the syntax look if you had let x = [{a: 1, b: 2, c: 3, z:26}, {a: 5, b: 6, c: 7, z:455}];Inapt
you can use Array.prototype.map() for array. let y = x.map(item => { /* remove one key from the item and return it */ })Mendelson
@RossAllen There is an option ignoreRestSiblings that was added in v3.15.0 (February 3, 2017). See: commit c59a0baMendelson
@IlyaPalkin Interesting. It feels a bit lazy though because it doesn't change the fact that there is a b in scope.Hushaby
If you're getting Module build failed: SyntaxError: Unexpected token, you probably need to add the babel rest spread transform plugin. See babeljs.io/docs/plugins/transform-object-rest-spreadExsiccate
What is this syntax called?Device
Destructuring assignment I thinkMendelson
Even though the opposite was never stated on this subject and this doesn't invalidate the solution, it might be worth noting that such feature performs a shallow copy: if x contained an object property, and since an object is a reference (codeburst.io/…), you could by mistake modify a nested value inside of x while modifying it in y.Lympho
As a side effect of what @RossAllen mentioned If you want to remove the same property from multiple objects you'll need to use var. Otherwise, you'll get an error for redeclaring a let variable. (In my case this came up in react for diffing state and nextState minus one property in componentShouldUpdate)Deformed
if bName - is the name of subject key - this does not work: let {[bName], ...y} = x; I am uninstalling node right now, it not capable of doing just what we want!!!!1111 (that was oneliner, one function)Uund
Thanks for the answer. You don't necessarily need Babel for this syntax anymore: caniuse.com/…Histrionic
To avoid "Unused variable" warning and still using the spread operator you can do: let y = {...x}; delete y.b;Rowan
I did a let { b: _b, ...y } = x; to skip the "Unused variable" warning.Hokanson
When the key you want to remove is dynamic do: const { [dynamicKey]: _, ...rest } = obj;Biome
S
274

I use this ESNext one liner

const obj = { a: 1, b: 2, c: 3, d: 4 }
const clone = (({ b, c, ...o }) => o)(obj) // remove b and c
console.log(clone)
Shashaban answered 23/11, 2018 at 8:55 Comment(11)
Instead of wrapping in array and map you can do: (({b, c, ...others}) => ({...others}))(obj)Nickname
@bucabay: Brilliant! That's the best answer of the lot! Standards compliant, works perfectly in Node etc. You should submit as an answer.Homemaker
@Homemaker There is a downside: readability. Especially when you compare it to solutions like _.omit(obj, ['b', 'c']).Smoke
@totymedli: Not by me. I'll take the syntactic form, part of standard ES6, over a magic function anytime, on readability grounds.Homemaker
@HaroenViaene If the expression used in your eval is not something that the user COULD potentially uses for evil intention it's fine, you have to be aware of the potential risk however you are right>Shashaban
A little bit too easy to hack… omit({}, "}));alert('gotcha!')(({a")Rochelle
@BrookJordan why would you hack your own code ? If the part of the code is not used in a user interaction suite, it's fine.Shashaban
That’s a big if. I don’t trust that any of my code in a shared codebase won’t have user generated text input at some point… I’m not sure I even trust that I won’t ever forget the internals of my code and put user content through it.Rochelle
Why using a function ? why not simply do : const { b, c, ...clone } = obj ?Archiearchiepiscopacy
@Simon Trichereau 's answer seems the easiest and most readable.Rhizo
Using a function ensures that the variables b and c do not stay assigned (or reassigned) in the current scope. A comment on the accepted answer mentioned that linters may even complain about that, if the variables are unused.Flavius
M
229
var clone = Object.assign({}, {a: 1, b: 2, c: 3});
delete clone.b;

or if you accept property to be undefined:

var clone = Object.assign({}, {a: 1, b: 2, c: 3}, {b: undefined});
Messiaen answered 9/1, 2016 at 21:7 Comment(5)
Simply deleting the property is a clear and simple way to do it.Wolfsbane
The caveat with delete is that it's not an immutable operation.Swinney
This is exactly what I was looking for, but a slightly more implemented in a slightly different way: var cake = {...currentCake, requestorId: undefined };Cherrylchersonese
@JavidJamae throwback to the times when everyone was obsessed about immutability of everythingSafar
@Safar You may not care about immutability these days, but many do. It is not an obsession; it is a good practice to get in the habit of doing because it results in code with fewer bug potentials.Mont
I
113

For those who can't use ES6, you can use lodash or underscore.

_.omit(x, 'b')

Or ramda.

R.omit('b', x)
Infeudation answered 13/1, 2017 at 4:16 Comment(5)
wouldn't it be more logical to use omit here? _.omit(x, 'b')Shakira
Thanks @tibalt. Updated the answer with it.Infeudation
delete is more concise--using lodash, underscore or ramda is only relevant to projects that already use and know them, otherwise this is increasingly irrelevant in 2018 and beyond.Libertinage
@Libertinage delete is already mentioned in other answers. There is no need for duplicate answers, don't you think? And of course, it is only relevant to those who already use lodash or ramda. And it is also only relevant to those stuck with ES5 and earlier as stated in this answer.Infeudation
@dashmug my previous comment was a criticism of the approach (not of your answer) that should be noted when choosing to use the approach represented in this answer. I would want this information if I was reading through the answers and is the reason for adding my comment and mentioning delete.Libertinage
C
95

To add to Ilya Palkin's answer: you can even dynamically remove keys:

const x = {a: 1, b: 2, c: 3, z:26};

const objectWithoutKey = (object, key) => {
  const {[key]: deletedKey, ...otherKeys} = object;
  return otherKeys;
}

console.log(objectWithoutKey(x, 'b')); // {a: 1, c: 3, z:26}
console.log(x); // {a: 1, b: 2, c: 3, z:26};

Demo in Babel REPL

Source:

Charentemaritime answered 20/11, 2016 at 0:55 Comment(3)
This is great, but is there a way to avoid the un-used var deletedKey? Not that it's causing any problems, but makes JSHint complain, and does seem odd since we really aren't using it.Scales
@JohnsonWong How about using _ which is allowed for a variable that you don't intend to use?Blastoderm
var b = {a:44, b:7, c:1}; let {['a']:z, ...others} = b; console.log(z , others ); // logs: 44, {b:7, c:1}Libertinage
E
35

You can write a simple helper function for it. Lodash has a similar function with the same name: omit

function omit(obj, omitKey) {
  return Object.keys(obj).reduce((result, key) => {
    if(key !== omitKey) {
       result[key] = obj[key];
    }
    return result;
  }, {});
}

omit({a: 1, b: 2, c: 3}, 'c')  // {a: 1, b: 2}

Also, note that it is faster than Object.assign and delete then: http://jsperf.com/omit-key

Endanger answered 10/1, 2016 at 12:7 Comment(4)
I would instead filter the array of keys and then reduce without the if-statement: Object.keys(obj).filter(key => key != omitKey).reduce((result, key) => ({...result, [key]: obj[key]}), {});Ochlocracy
Using it, as it is faster than Object.assignCythiacyto
@Ochlocracy Spreading the result like that is incredibly slow, and even if you do it properly filter+reduce is still slower than just reduce, and both are slower than Object.assign+delete if the object has more than 20 properties: jsbench.me/x9l9ctn9wz/1Aviary
And reduce is even worse than Object.assign+delete if you're removing multiple keys, because then it has to check omitKeys.includes(key) on every iteration instead of just once at the endAviary
J
22

Here's an option for omitting dynamic keys that I believe has not been mentioned yet:

const obj = { 1: 1, 2: 2, 3: 3, 4: 4 };
const removeMe = 1;

const { [removeMe]: removedKey, ...newObj } = obj;

removeMe is aliased as removedKey and ignored. newObj becomes { 2: 2, 3: 3, 4: 4 }. Note that the removed key does not exist, the value was not just set to undefined.

Justiciable answered 2/10, 2019 at 17:31 Comment(0)
I
18

Maybe something like this:

var copy = Object.assign({}, {a: 1, b: 2, c: 3})
delete copy.c;

Is this good enough? Or can't c actually get copied?

Irredentist answered 9/1, 2016 at 21:6 Comment(0)
G
16

Using Object Destructuring

const omit = (prop, { [prop]: _, ...rest }) => rest;
const obj = { a: 1, b: 2, c: 3 };
const objWithoutA = omit('a', obj);
console.log(objWithoutA); // {b: 2, c: 3}
Guiana answered 23/3, 2019 at 0:14 Comment(2)
I guess this solution is meant to prevent the 'Unused variable' warning in JSLint. Unfortunately, using _ doesn't solve the issue for ESLint...Isotone
The eslint rule you need is "no-unused-vars": ["error", { "args": "all", "argsIgnorePattern": "^_" } ].Strongminded
C
9

I am using object destructuring here. I have separated the password into variables and the rest variables contain all object properties and values except the password. NOTE: rest is not fixed keyword here you can name it accordingly

const obj = {name:"john", password:"Abcds12@", id:"125455"}
const {password,...rest} = obj;
console.log(rest);
Cosimo answered 17/12, 2022 at 8:37 Comment(2)
It would be nice to give a brief explanation of how this works / how it solves the problem, and how it's different than existing answers.Godesberg
I am using object destructuring here. I have separated the password into variables and the rest variables contain all object properties and values except the password. NOTE: rest is not fixed keyword here you can name it accordinglyCosimo
S
6

Hey seems like you run in to reference issues when you're trying to copy an object then deleting a property. Somewhere you have to assign primitive variables so javascript makes a new value.

Simple trick (may be horrendous) I used was this

var obj = {"key1":"value1","key2":"value2","key3":"value3"};

// assign it as a new variable for javascript to cache
var copy = JSON.stringify(obj);
// reconstitute as an object
copy = JSON.parse(copy);
// now you can safely run delete on the copy with completely new values
delete copy.key2

console.log(obj)
// output: {key1: "value1", key2: "value2", key3: "value3"}
console.log(copy)
// output: {key1: "value1", key3: "value3"}
Stockjobber answered 7/6, 2018 at 13:44 Comment(1)
I actually kind of like it. Could do JSON.parse(JSON.stringify(Object.assign({}, obj, { key2: undefined })));. Don't even have to delete it, just needs a falsy value.Meitner
F
6

You also can use spread operator to do this

const source = { a: 1, b: 2, c: 3, z: 26 }
const copy = { ...source, ...{ b: undefined } } // { a: 1, c: 3, z: 26 }
Faletti answered 12/9, 2018 at 10:15 Comment(4)
Seems super nifty. This however keeps the key as undefined in copyBurtburta
so you could remove the undefined keys if there are the only ones to be in thereHarwin
not sure why you did the extra spreading in the copy, const copy = { ...source, b: undefined } boils down to exactly the same.Isotone
As mentioned before, Object.keys(copy) would still return ['a', 'b', 'c', 'z'] which is not always what you want.Wondrous
P
5

What about this? I never found this patter around but I was just trying to exclude one or more properties without the need of creating an extra object. This seems to do the job but there are some side effects I'm not able to see. For sure is not very readable.

const postData = {
   token: 'secret-token',
   publicKey: 'public is safe',
   somethingElse: true,
};

const a = {
   ...(({token, ...rest} = postData) => (rest))(),
}

/**
a: {
   publicKey: 'public is safe',
   somethingElse: true,
}
*/
Pardner answered 19/3, 2019 at 12:8 Comment(0)
I
5

The solutions above using structuring do suffer from the fact that you have an used variable, which might cause complaints from ESLint if you're using that.

So here are my solutions:

const src = { a: 1, b: 2 }
const result = Object.keys(src)
  .reduce((acc, k) => k === 'b' ? acc : { ...acc, [k]: src[k] }, {})

On most platforms (except IE unless using Babel), you could also do:

const src = { a: 1, b: 2 }
const result = Object.fromEntries(
  Object.entries(src).filter(k => k !== 'b'))
Isotone answered 6/6, 2019 at 17:15 Comment(0)
P
4

Lodash omit

let source = //{a: 1, b: 2, c: 3, ..., z:26}
let copySansProperty = _.omit(source, 'b');
// {a: 1, c: 3, ..., z:26}
Pulver answered 5/10, 2017 at 15:44 Comment(0)
M
4

I accomplished it this way, as an example from my Redux reducer:

 const clone = { ...state };
 delete clone[action.id];
 return clone;

In other words:

const clone = { ...originalObject } // note: original object is not altered
delete clone[unwantedKey]           // or use clone.unwantedKey or any other applicable syntax
return clone                        // the original object without the unwanted key
Myth answered 28/4, 2019 at 22:31 Comment(2)
Seems like a lot of extra work, compared to the accepted answer from 3 years ago (and both options rely on transpiling to for many browsers).Domesday
I have a similar use case (reducer) so I came up with a nice way that supports dynamic keys without mutation. Basically: const { [removeMe]: removedKey, ...newObj } = obj; - see my answer on this question.Justiciable
S
4
const x = {obj1: 1, pass: 2, obj2: 3, obj3:26};

const objectWithoutKey = (object, key) => {
  const {[key]: deletedKey, ...otherKeys} = object;
  return otherKeys;
}

console.log(objectWithoutKey(x, 'pass'));
Seavey answered 11/5, 2020 at 11:33 Comment(1)
While this code may provide a solution to the question, it's better to add context as to why/how it works. This can help future users learn, and apply that knowledge to their own code. You are also likely to have positive feedback from users in the form of upvotes, when the code is explained.Labile
N
3

If you're dealing with a huge variable, you don't want to copy it and then delete it, as this would be inefficient.

A simple for-loop with a hasOwnProperty check should work, and it is much more adaptable to future needs :

for(var key in someObject) {
        if(someObject.hasOwnProperty(key) && key != 'undesiredkey') {
                copyOfObject[key] = someObject[key];
        }
}
Netti answered 10/8, 2017 at 13:40 Comment(1)
The spread operator solution does exactly the same with far nicer syntax.Homemaker
R
2

I don't know exactly what you want to use this for, so I'm not sure if this would work for you, but I just did the following and it worked for my use case:

const newObj ={...obj, [key]: undefined}
Raina answered 27/12, 2020 at 16:15 Comment(1)
This way you will get object which has the [key] property with value = undefined. This is different than object not having the [key] property. E.g. Object.keys() will give you different results on these objects.Esther
C
1

The delete keyword solution will throw a compilation error if you're using typescript, because it breaks the contract of the object you instantiated. And the ES6 spread operator solution (const {x, ...keys} = object) may throw an error depending on the linting configuration you're using on your project, since the x variable is not beign used. So I came up with this solution:

const cloneObject = Object.entries(originalObject)
    .filter(entry => entry[0] !== 'keyToRemove')
    .reduce((acc, keyValueTuple) => ({ ...acc, [keyValueTuple[0]]: keyValueTuple[1] }), {});

It solves the problem using the combination of Object.entries method (to get an array of key/value pairs of the original object) and the array methods filter and reduce. It looks verbose, but I think it's nice to have a one line chainable solution.

Caron answered 25/5, 2021 at 21:24 Comment(0)
A
0

Here are my two cents, on Typescript, slightly derived from @Paul's answer and using reduce instead.

function objectWithoutKey(object: object, keys: string[]) {
    return keys.reduce((previousValue, currentValue) => {
        // @ts-ignore
        const {[currentValue]: undefined, ...clean} = previousValue;
        return clean
    }, object)
}

// usage
console.log(objectWithoutKey({a: 1, b: 2, c: 3}, ['a', 'b']))
Anastigmatic answered 16/8, 2020 at 19:21 Comment(0)
O
0

I have one object named: options with some keys

  let options = {       
        userDn: 'somePropertyValue',
        username: 'someValue',
        userSearchBase: 'someValue',
        usernameAttribute: 'uid',
        userPassword: 'someValue'
    }

I want to log all object excelp userPassword Property because Im testing something, I am using this code:

console.log(Object.keys(options).map(x => x + ': ' + (x === "userPassword" ? '---' : options[x])));

If you want to make it dynamic, just make a function and instead of explicitly putting userPassword you can place the value of the property you want to exclude

Odontalgia answered 22/10, 2020 at 9:42 Comment(0)
M
0

Clean and fast using lodash, With this solution, you are able to remove multiple keys and also without changing the original object. This is more extendable and more usable in different situations:

import * as _ from 'lodash';

function cloneAndRemove(
    removeTheseKeys: string[],
    cloneObject: any,
): object | never {
    const temp = _.cloneDeep(cloneObject);
    removeTheseKeys.forEach((key) => {
        delete temp[key];
    });

    return temp;
}

export { cloneAndRemove };
Misesteem answered 9/8, 2021 at 14:19 Comment(0)
G
-1

Here is a Typescript method to do that. Expects 2 arguments, the object and an array of strings containing keys to be removed:

removeKeys(object: { [key: string]: any }, keys: string[]): { [key: string]: any } {
  const ret: { [key: string]: any } = {};

  for (const key in object) {
    if (!keys.includes(key)) {
      ret[key] = object[key];
    }
  }

  return ret;
}
Ginsberg answered 19/11, 2021 at 8:17 Comment(0)
I
-3

You can use lodash library for this purpose (definitely if you need to work with lots of arrays/array of objects/objects in a project).

Using lodash deepclone function You can do:

const _obj = _.cloneDeep(obj);
delete _obj.key;

First clones the whole object into new object and then delete the desired key from the cloned object so that the original one doesn't get affected.

Idioglossia answered 5/3, 2021 at 19:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.