¿Cómo puedo clonar un objeto JavaScript excepto una clave?


308

Tengo un objeto JS plano:

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

Quiero clonar el objeto excepto por un elemento:

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

¿Cuál es la forma más fácil de hacer esto (prefiriendo usar es6 / 7 si es posible)?


Sin cambiar el objeto original: JSON.parse (JSON.stringify ({... obj, 'key2': undefined}))
infinity1975

Respuestas:


441

Si usa Babel , puede usar la siguiente sintaxis para copiar la propiedad b de x en la variable b y luego copiar el resto de las propiedades en la variable y :

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

y se trasladará a:

"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"]);

58
Si limita su código para variables no utilizadas, esto dará como resultado una "variable no utilizada 'b'". advertencia sin embargo.
Ross Allen

1
¿cómo sería el aspecto de sintaxis si tuvieralet x = [{a: 1, b: 2, c: 3, z:26}, {a: 5, b: 6, c: 7, z:455}];
ke3pup

14
@RossAllen Hay una opción ignoreRestSiblingsque se agregó en v3.15.0 (3 de febrero de 2017). Ver: commit c59a0ba
Ilya Palkin

44
@IlyaPalkin Interesante. Sin embargo, se siente un poco flojo porque no cambia el hecho de que hay un balcance.
Ross Allen

2
Si está obteniendo un error en la compilación del módulo: SyntaxError: token inesperado, probablemente deba agregar el complemento de transformación de extensión de resto de babel. Ver babeljs.io/docs/plugins/transform-object-rest-spread
jsaven el

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

o si acepta que la propiedad no esté definida:

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

77
Simplemente eliminar la propiedad es una forma clara y sencilla de hacerlo.
Show el

24
La advertencia con eliminar es que no es una operación inmutable.
Javid Jamae

1
esto mantiene la clave
Fareed Alnamrouti

1
Mi comentario fue escrito antes de editar su respuesta y agregar la declaración de eliminación
Fareed Alnamrouti

Esto es exactamente lo que estaba buscando, pero un poco más implementado de una manera ligeramente diferente: var cake = {... currentCake, requestorId: undefined};
abelito

73

Para agregar a la respuesta de Ilya Palkin: incluso puede eliminar claves dinámicamente:

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 en Babel REPL

Fuente:


3
Esta es una buena sintaxis.
Hinrich

2
Esto es genial, pero ¿hay alguna forma de evitar la varKeyKey no utilizada? No es que esté causando ningún problema, pero hace que JSHint se queje, y parece extraño ya que realmente no lo estamos usando.
Johnson Wong

66
@JohnsonWong ¿Qué tal usar _cuál está permitido para una variable que no tiene intención de usar?
Ryan H.

var b = {a:44, b:7, c:1}; let {['a']:z, ...others} = b; console.log(z , others ); // logs: 44, {b:7, c:1}
jimmont

70

Para aquellos que no pueden usar ES6, pueden usar lodasho underscore.

_.omit(x, 'b')

O ramda.

R.omit('b', x)

66
¿no sería más lógico usar omitir aquí? _.omit(x, 'b')
tibalt

Gracias @tibalt. Se actualizó la respuesta con ella.
Noel Llevares

eliminar es más conciso: usar lodash, subrayado o ramda solo es relevante para proyectos que ya los usan y los conocen, de lo contrario, esto es cada vez más irrelevante en 2018 y más allá.
jimmont

1
@jimmont delete ya se menciona en otras respuestas. No hay necesidad de respuestas duplicadas, ¿no crees? Y, por supuesto, solo es relevante para aquellos que ya usan lodash o ramda. Y también solo es relevante para aquellos atrapados con ES5 y anteriores como se indica en esta respuesta.
Noel Llevares

@dashmug mi comentario anterior fue una crítica del enfoque (no de su respuesta) que debe tenerse en cuenta al elegir utilizar el enfoque representado en esta respuesta. Quisiera esta información si estuviera leyendo las respuestas y es la razón para agregar mi comentario y mencionardelete .
jimmont

63

Yo uso este 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)


Si necesita una función de propósito general:

function omit(obj, props) {
  props = props instanceof Array ? props : [props]
  return eval(`(({${props.join(',')}, ...o}) => o)(obj)`)
}

// usage
const obj = { a: 1, b: 2, c: 3, d: 4 }
const clone = omit(obj, ['b', 'c'])
console.log(clone)


99
En lugar de envolver en matriz y mappuede hacer:(({b, c, ...others}) => ({...others}))(obj)
bucabay

@bucabay: ¡Brillante! Esa es la mejor respuesta del lote! Cumple con los estándares, funciona perfectamente en Node, etc. Debe enviarlo como respuesta.
david.pfx

3
Fantástica respuesta amigo ... <3
Ajithkumar S

55
@totymedli: No por mí. Tomaré la forma sintáctica, parte del ES6 estándar, sobre una función mágica en cualquier momento, por motivos de legibilidad.
david.pfx

1
Brillante. Eso es todo.
darksoulsong

22

Puede escribir una función auxiliar simple para ello. Lodash tiene una función similar con el mismo nombre: omitir

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}

Además, tenga en cuenta que es más rápido que Object.assign y elimine luego: http://jsperf.com/omit-key


11

Tal vez algo como esto:

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

¿Es esto lo suficientemente bueno? ¿O no puede cser copiado?


11

Usando la Desestructuración de Objetos

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}


Gran solución!
Denys Mikhalenko

2
Supongo que esta solución está destinada a evitar la advertencia de 'variable no utilizada' en JSLint. Desafortunadamente, el uso _no resuelve el problema de ESLint ...
bert bruynooghe

6

Oye, parece que te encuentras con problemas de referencia cuando intentas copiar un objeto y luego eliminar una propiedad. En algún lugar debe asignar variables primitivas para que JavaScript cree un nuevo valor.

Truco simple (puede ser horrible) que usé fue esto

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"}

De hecho, me gusta un poco. Podría hacer JSON.parse(JSON.stringify(Object.assign({}, obj, { key2: undefined })));. Ni siquiera tiene que eliminarlo, solo necesita un valor falso.
Chad

6

Aquí hay una opción para omitir claves dinámicas que creo que aún no se ha mencionado:

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

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

removeMees alias removedKeye ignorado. newObjse convierte { 2: 2, 3: 3, 4: 4 }. Tenga en cuenta que la clave eliminada no existe, el valor no solo se estableció en undefined.


Buena solución Tuve un caso de uso similar (clave dinámica en reductor).
ericgio

4

LA MANERA MÁS FÁCIL

const allAlphabets = {a: 1, b: 2, c: 3, ..., z:26};
const { b, ...allExceptOne } = allAlphabets;
console.log(allExceptOne);   // {a: 1, c: 3, ..., z:26}

3

Lodash omitir

let source = //{a: 1, b: 2, c: 3, ..., z:26}
let copySansProperty = _.omit(source, 'b');
// {a: 1, c: 3, ..., z:26}

sí, Lodash es una opción elegante, pero estaba tratando de lograr lo mismo con Vanilla ES6
andreasonny83

3

También puede usar el operador de propagación para hacer esto

const source = { a: 1, b: 2, c: 3, z: 26 }
const copy = { ...source, ...{ b: undefined } } // { a: 1, c: 3, z: 26 }

2
Parece super ingenioso. Sin embargo, esto mantiene la clave como indefinidacopy
kano

para que puedas eliminar las claves indefinidas si hay las únicas que están allí
Victor

1
No estoy seguro de por qué hizo la extensión adicional en la copia, se const copy = { ...source, b: undefined } reduce a exactamente lo mismo.
bert bruynooghe

3

Las soluciones anteriores que usan estructuración adolecen del hecho de que tiene una variable usada, lo que podría causar quejas de ESLint si está usando eso.

Así que aquí están mis soluciones:

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

En la mayoría de las plataformas (excepto IE a menos que use Babel), también podría hacer:

const src = { a: 1, b: 2 }
const result = Object.fromEntries(
  Object.entries(src).filter(k => k !== 'b'))


2

Si se trata de una variable enorme, no desea copiarla y luego eliminarla, ya que esto sería ineficiente.

Un bucle for simple con una comprobación hasOwnProperty debería funcionar, y es mucho más adaptable a las necesidades futuras:

for(var key in someObject) {
        if(someObject.hasOwnProperty(key) && key != 'undesiredkey') {
                copyOfObject[key] = someObject[key];
        }
}

1
La solución de operador de propagación hace exactamente lo mismo con una sintaxis mucho mejor.
david.pfx

2

¿Qué hay de esto? Nunca encontré este patrón, pero solo estaba tratando de excluir una o más propiedades sin la necesidad de crear un objeto adicional. Esto parece hacer el trabajo, pero hay algunos efectos secundarios que no puedo ver. Por supuesto, no es muy legible.

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

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

/**
a: {
   publicKey: 'public is safe',
   somethingElse: true,
}
*/

2

Lo logré de esta manera, como un ejemplo de mi reductor Redux:

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

En otras palabras:

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

Parece mucho trabajo extra, en comparación con la respuesta aceptada de hace 3 años (y ambas opciones dependen de la transpiración para muchos navegadores).
carpiediem

Tengo un caso de uso similar (reductor), así que se me ocurrió una forma agradable que admite teclas dinámicas sin mutación. Básicamente: const { [removeMe]: removedKey, ...newObj } = obj;- vea mi respuesta a esta pregunta.
goldins

1

Recientemente lo hice de esta manera muy simple:

const obj = {a: 1, b: 2, ..., z:26};

simplemente usando el operador spread para separar la propiedad no deseada:

const {b, ...rest} = obj;

... y object.assign para tomar solo la parte 'rest':

const newObj = Object.assign({}, {...rest});

3
restya es un objeto nuevo, no necesita la última línea. Además, esto es idéntico a la solución aceptada.
carpiediem

0
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'));

1
Si bien este código puede proporcionar una solución a la pregunta, es mejor agregar contexto sobre por qué / cómo funciona. Esto puede ayudar a los futuros usuarios a aprender y aplicar ese conocimiento a su propio código. También es probable que reciba comentarios positivos de los usuarios en forma de votos a favor, cuando se explique el código.
borchvm
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.