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.