Como clonar um objeto JavaScript, exceto uma chave?


308

Eu tenho um objeto JS simples:

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

Eu quero clonar o objeto, exceto um elemento:

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

Qual é a maneira mais fácil de fazer isso (preferindo usar o es6 / 7, se possível)?


Sem alterar o objeto original: JSON.parse (JSON.stringify ({... obj, 'key2': undefined}))
— infinity1975

Respostas:


441

Se você usar Babel, poderá usar a seguinte sintaxe para copiar a propriedade b de x na variável be, em seguida, copiar o restante das propriedades na variável y :

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

e será transpilado para:

"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
Se você codificar seu código para variáveis ​​não utilizadas, isso resultará em uma "variável não utilizada 'b'". aviso embora.
— Ross Allen

1
como seria a aparência de sintaxe se você tevelet x = [{a: 1, b: 2, c: 3, z:26}, {a: 5, b: 6, c: 7, z:455}];
— ke3pup

14
@RossAllen Há uma opção ignoreRestSiblingsque foi adicionada na v3.15.0 (3 de fevereiro de 2017). Veja: commit c59a0ba
— Ilya Palkin

4
@IlyaPalkin Interesting. Parece um pouco preguiçoso, porque não muda o fato de que existe um bescopo.
— Ross Allen

2
Se você estiver obtendo uma falha na compilação do módulo: SyntaxError: token inesperado, provavelmente precisará adicionar o plug-in babel rest spread transform. Veja babeljs.io/docs/plugins/transform-object-rest-spread
— jsaven

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

ou se você aceitar que a propriedade seja indefinida:

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

7
Simplesmente excluir a propriedade é uma maneira clara e simples de fazer isso.
— Sshow 06/07/19

24
A ressalva com a exclusão é que não é uma operação imutável.
— JavaScript é necessário

1
isso mantém a chave
— Fareed Alnamrouti 27/1017

1
Meu comentário foi escrito antes de editar a sua resposta e adicione a instrução de exclusão
— Fareed Alnamrouti

Era exatamente isso que eu estava procurando, mas um pouco mais implementado de uma maneira um pouco diferente: var cake = {... currentCake, requestorId: undefined};
— Abelito 18/04/19

73

Para adicionar à resposta de Ilya Palkin: você pode remover dinamicamente as chaves:

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

Demonstração em Babel REPL

Fonte:


3
Essa é uma boa sintaxe.
— Hinrich

2
Isso é ótimo, mas existe uma maneira de evitar a varkeyKey não usada? Não que esteja causando problemas, mas faz com que o JSHint se queixe, e parece estranho, pois realmente não o estamos usando.
— Johnson Wong

6
@JohnsonWong Que tal usar o _que é permitido para uma variável que você não pretende 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 aqueles que não podem usar o ES6, você pode usar lodashou underscore.

_.omit(x, 'b')

Or ramda.

R.omit('b', x)

6
não seria mais lógico usar omitir aqui? _.omit(x, 'b')
— tibalt

Obrigado @tibalt. Atualizado a resposta com ele.
— 22617 Noel Llevares

delete é mais conciso - usar lodash, underscore ou ramda é relevante apenas para projetos que já os utilizam e os conhecem; caso contrário, isso é cada vez mais irrelevante em 2018 e além.
— precisa saber é o seguinte

1
O @jimmont delete já foi mencionado em outras respostas. Não há necessidade de respostas duplicadas, você não acha? E, claro, isso é relevante apenas para aqueles que já usam lodash ou ramda. E isso também é relevante apenas para aqueles presos no ES5 e anteriores, conforme declarado nesta resposta.
— Noel Llevares 10/09/18

@dashmug meu comentário anterior foi uma crítica à abordagem (não à sua resposta) que deve ser observada ao optar por usar a abordagem representada nesta resposta. Gostaria dessas informações se estivesse lendo as respostas e seja o motivo para adicionar meu comentário e menção delete.
— Jimmont 19/09

63

Eu uso este forro ESNext one

const obj = { a: 1, b: 2, c: 3, d: 4 }
const clone = (({ b, c, ...o }) => o)(obj) // remove b and c
console.log(clone)


Se você precisar de uma função de uso geral:

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)


9
Em vez de agrupar em array, mapvocê pode:(({b, c, ...others}) => ({...others}))(obj)
— bucabay 22/12

@bucabay: Brilhante! Essa é a melhor resposta do lote! Em conformidade com os padrões, funciona perfeitamente no Nó etc. Você deve enviar como resposta.
— David.pfx

3
Resposta fantástica mate .. <3 #
— Ajithkumar S

5
@totymedli: Não por mim. Eu assumirei a forma sintática, parte do padrão ES6, sobre uma função mágica a qualquer momento, por motivos de legibilidade.
— David.pfx #

1
Brilhante. Isso é tudo.
— darksoulsong

22

Você pode escrever uma função auxiliar simples para ela. Lodash tem uma função semelhante com o mesmo nome: 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}

Além disso, observe que é mais rápido que Object.assign e exclua: http://jsperf.com/omit-key


11

Talvez algo parecido com isto:

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

Isso é bom o suficiente? Ou não pode crealmente ser copiado?


11

Usando a Reestruturação 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}


Ótima solução!
— Denys Mikhalenko

2
Eu acho que essa solução tem como objetivo evitar o aviso 'Variável não utilizada' no JSLint. Infelizmente, usando _não resolve o problema para ESLint ...
— bert bruynooghe

6

Ei, parece que você encontra problemas de referência quando tenta copiar um objeto e excluir uma propriedade. Em algum lugar você precisa atribuir variáveis ​​primitivas para que o javascript crie um novo valor.

Truque simples (pode ser horrível) eu usei foi este

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

Na verdade, eu até que gosto disso. Poderia fazer JSON.parse(JSON.stringify(Object.assign({}, obj, { key2: undefined })));. Nem precisa excluí-lo, apenas precisa de um valor falso.
— Chad

6

Aqui está uma opção para omitir chaves dinâmicas que, acredito, ainda não foram mencionadas:

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

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

removeMeé alias removedKeye ignorado. newObjtorna-se { 2: 2, 3: 3, 4: 4 }. Observe que a chave removida não existe, o valor não foi apenas definido como undefined.


Ótima solução. Eu tive um caso de uso semelhante (chave dinâmica no redutor).
— ericgio 04/12/19

4

CAMINHO MAIS 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

Omitir Lodash

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

sim, Lodash é uma opção elegante, mas eu estava tentando conseguir o mesmo com baunilha ES6
— andreasonny83

3

Você também pode usar o operador spread para fazer isso

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

2
Parece super bacana. Este, porém, mantém a chave como indefinido nacopy
— Kano

para que você possa remover as chaves indefinidas, se houver as únicas disponíveis lá #
— Victor Victor

1
não sei por que você fez a propagação extra na cópia, const copy = { ...source, b: undefined } resume-se exatamente ao mesmo.
— bert bruynooghe

3

As soluções acima usando a estruturação sofrem com o fato de você ter uma variável usada, o que pode causar reclamações do ESLint se você a estiver usando.

Então, aqui estão minhas soluções:

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

Na maioria das plataformas (exceto no IE, a menos que você use o Babel), você também pode:

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


2

Se você está lidando com uma variável enorme, não deseja copiá-la e excluí-la, pois isso seria ineficiente.

Um loop for simples com uma verificação hasOwnProperty deve funcionar e é muito mais adaptável às necessidades futuras:

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

1
A solução do operador de propagação faz exatamente o mesmo com uma sintaxe muito melhor.
— David.pfx

2

Que tal isso? Eu nunca encontrei esse padrão, mas estava apenas tentando excluir uma ou mais propriedades sem a necessidade de criar um objeto extra. Isso parece funcionar, mas existem alguns efeitos colaterais que não consigo ver. Com certeza não é muito legível.

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

Consegui dessa maneira, como um exemplo do meu redutor Redux:

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

Em outras palavras:

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 muito trabalho extra, em comparação com a resposta aceita de três anos atrás (e as duas opções dependem da transpilação para muitos navegadores).
— Carpiediem

Como tenho um caso de uso semelhante (redutor), criei uma boa maneira de oferecer suporte a chaves dinâmicas sem mutação. Basicamente: const { [removeMe]: removedKey, ...newObj } = obj;- veja minha resposta sobre esta pergunta.
— goldins 2/10/19

1

Eu fiz recentemente dessa maneira muito simples:

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

apenas usando o operador spread para separar a propriedade indesejada:

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

... e object.assign para pegar apenas a parte 'rest':

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

3
restjá é um novo objeto - você não precisa da última linha. Além disso, isso é idêntico à solução aceita.
— 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
Embora esse código possa fornecer uma solução para a pergunta, é melhor adicionar contexto ao porquê / como ele funciona. Isso pode ajudar os usuários futuros a aprender e aplicar esse conhecimento ao seu próprio código. Também é provável que você tenha um feedback positivo dos usuários na forma de upvotes, quando o código for explicado.
— borchvm
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.