Como obter um subconjunto das propriedades de um objeto javascript
Digamos que eu tenho um objeto:
Eu quero criar um novo objeto com um subconjunto de suas propriedades.
— Christian Schlensker
A biblioteca sublinhado tem um monte de funções auxiliares como este, confira: underscorejs.org/#pick
Eu pensei que disse emo.sliceà primeira vista.
— Bill Criswell
Pensando bem ... eu não vou criar um subconjunto ...
Usando a destruição de objetos e a abreviação de propriedades
Partida Philipp Kewisch:
Esta é realmente apenas uma função anônima sendo chamada instantaneamente. Tudo isso pode ser encontrado na página Destructuring Assignment no MDN. Aqui está um formulário expandido
— Ivan Nosov
Como você aprendeu sobre como fazer isso? Em nenhum lugar nos documentos ou artigos que eu vi (incluindo MDN) ele mostra a sintaxe da seta sendo usada na Reestruturação de Objetos. É muito bom saber disso.
— Papiro
Esta é realmente apenas uma função anônima sendo chamada instantaneamente. Tudo isso pode ser encontrado na página Destructuring Assignment no MDN. Aqui está uma forma expandida: let unwrap = ({a, c}) => ({a, c}); let unwrap2 = function({a, c}) { return { a, c }; }; let picked = unwrap({ a: 5, b: 6, c: 7 });
— Philipp Kewisch 27/01
existe uma maneira de fazer isso dinamicamente com o operador de spread?
@ TomSarduy, você pode usar o resto se quiser especificar quais objetos remover, por exemplo const { b, ...picked } = object, criaria pickedcomo { a: 5, c: 7 }. Você especificou simplesmente para remover b. Seu eslint provavelmente ficará chateado com você por declarar um var que você não está usando.
— Josh de Qaribou 28/08
Uma desvantagem aqui é que você precisa digitar totalmente a série de nomes de atributos duas vezes. Isso pode ser um problema nos casos em que muitos atributos precisam ser escolhidos.
Sugiro dar uma olhada no Lodash ; possui muitas funções excelentes de utilidade.
Por exemplo, pick()seria exatamente o que você procura:
— Ygg
o mesmo para underscore.js
Existe alguma função para excluir apenas determinados campos em vez de selecionar? então eu tenho cerca de 50 campos no meu json e quero tudo, exceto apenas 2 campos.
— Shrikant Prabhu
Sim! você pode usar _.omit(elmo, ['voice'])para retornar tudo, excetovoice
— xavdid
o que eu não gosto nessa abordagem é que você coloca os nomes dos campos entre aspas, para que sejam suscetíveis a erros de digitação, refatorações comuns como renomear uma propriedade no IDE não serão atendidas etc.
Se você estiver usando o ES6, há uma maneira muito concisa de fazer isso usando a desestruturação. A reestruturação permite adicionar facilmente objetos usando uma propagação, mas também permite criar objetos de subconjunto da mesma maneira.
isso funciona apenas para remover um campo, não para selecionar um subconjunto conhecido? potencialmente um número infinito de campos desconhecidos de remover, mas pode ser que algumas pessoas estão procurando
É verdade, mas ele pode remover vários campos conhecidos que podem ser reatribuídos para um novo objeto, para que ainda pareça relevante para esta pergunta. Adicionado à resposta para ilustrar melhor.
— 27418 Lauren
Este é essencialmente o mesmo que o que está em como a resposta de @Ivan Nosov , embora é explicado de uma forma mais compreensível aqui
Embora seja um pouco mais detalhado, você pode realizar o que todo mundo recomendou o sublinhado / lodash por 2 anos atrás, usando Array.prototype.reduce .
Essa abordagem resolve do outro lado: em vez de pegar um objeto e passar nomes de propriedades para extrair, pegar uma matriz de nomes de propriedades e reduzi-los a um novo objeto.
Embora seja mais detalhado no caso mais simples, um retorno de chamada aqui é bastante útil, pois você pode atender facilmente a alguns requisitos comuns, por exemplo, altere a propriedade 'color' para 'color' no novo objeto, aplainar matrizes etc. - as coisas que você precisa fazer ao receber um objeto de um serviço / biblioteca e criar um novo objeto necessário em outro lugar. Embora o sublinhado / lodash sejam excelentes e bem implementadas, esta é a minha abordagem preferida para menos confiança do fornecedor e uma abordagem mais simples e consistente quando minha lógica de criação de subconjuntos fica mais complexa.
edit: es7 versão do mesmo:
editar: Um bom exemplo para curry também! Faça com que uma função 'pick' retorne outra função.
O acima é bem parecido com o outro método, exceto que permite criar um 'selecionador' em tempo real. por exemplo
O que é especialmente interessante sobre essa abordagem é que você pode facilmente passar as 'escolhas' escolhidas para qualquer coisa que desempenhe uma função, por exemplo Array#map:
— Josh de Qaribou
ES6 torna possível para que isso seja ainda mais limpa através de funções de seta, e o retorno de Object.assign (desde a atribuição de uma propriedade de objeto retorna o valor da propriedade, mas Object.assign retorna o objeto.)
— Josh partir Qaribou
Outra observação importante: você raramente precisará mais fazer isso, uma vez que normalmente apenas desestrutura a atribuição ou os argumentos. por exemplo function showToy({ color, height }) {, colocaria apenas o que você precisa em escopo. A reduceabordagem faz sentido principalmente quando você está simplificando objetos para serialização.
— Josh de Qaribou 08/04
Essa versão do ES6 tem menos desempenho, porque faz uma cópia de todas as propriedades a cada iteração. Torna uma operação O (n) em O (n ^ 2). Um ES6 equivalente de seu primeiro bloco de código seriaconst pick = (obj, props) => props.reduce((a, e) => (a[e] = obj[e], a), {});
— 4castle
@ 4castle sim boa chamada - não faz sentido iterar tanto. Gosto da sintaxe da vírgula - melhor do que um monte de retornos.
@ShevchenkoViktor Na verdade, eu usei essa abordagem na minha versão original do es6, mas a mudei após o comentário do @castle. Eu acho que a propagação é mais clara, mas é uma grande diferença para objetos maiores no código que podem estar facilmente em um gargalo (por exemplo, atrasar a renderização dos dados retornados fetch), então eu recomendo adicionar um comentário explicando o uso do operador de vírgula.
ES6 desestruturação
A sintaxe da reestruturação permite desestruturar e recombinar um objeto, com parâmetros ou variáveis ​​de função.
A limitação é que uma lista de chaves é predefinida, elas não podem ser listadas como seqüências de caracteres, conforme mencionado na pergunta. A reestruturação se torna mais complicada se uma chave não é alfanumérica, por exemplo foo_bar.
A desvantagem é que isso requer duplicar uma lista de chaves, resultando em código detalhado, caso uma lista seja longa. Como a desestruturação duplica a sintaxe literal do objeto nesse caso, uma lista pode ser copiada e colada como está.
A vantagem é que é uma solução de alto desempenho que é natural para o ES6.
Variáveis ​​temporárias
A lista arbitrária de chaves selecionadas consiste em seqüências de caracteres, conforme a pergunta. Isso permite não predefini-los e usar variáveis ​​que contêm nomes de chave, comopick(obj, 'foo', someKey, ...moreKeys) .
Um one-liner fica mais curto a cada edição JS.
Ou com o operador de vírgula:
O ECMAScript 2017 possui Object.entriese Array.prototype.includes, o ECMAScript 2019 possui Object.fromEntries, eles podem ser polyfilled quando necessário e facilitar a tarefa:
Um one-liner pode ser reescrito como função auxiliar semelhante ao Lodashpick ou omitonde a lista de chaves é passada através de argumentos:
Uma observação sobre falta de chaves
A principal diferença entre a desestruturação e a função convencional do tipo Lodash pické que a desestruturação inclui chaves selecionadas inexistentes com undefinedvalor em um subconjunto:
Esse comportamento pode ou não ser desejável. Não pode ser alterado para desestruturar a sintaxe.
Enquanto pickpode ser alterado para incluir chaves ausentes, iterando uma lista de chaves selecionadas:
Que pena que essa resposta seja tão recente e, portanto, não receba a exposição que merece. Na IMO, deve ser a resposta aceita por completude, simplicidade, versatilidade e praticidade. Manterei a versão ES6 na minha biblioteca de trechos mais úteis.
— 21819 VanAlbert
ES5 - "Referência não
— detectadoErro
@NikhilVartak Espera-se que a objvariável seja uma variável que armazene um objeto. Se o nome da sua variável for diferente, use-o em vez de obj.
Foi mal! Eu esqueci Object.keys(obj). Sonolento.
— Nikhil Vartak
Não há nada como isso embutido na biblioteca principal, mas você pode usar a destruição de objetos para fazer isso ...
Você também pode escrever uma função utilitária fazê-lo ...
Bibliotecas como o Lodash também possuem _.pick().
ótimo, eu apenas tive que alterar o forEach para: keys.forEach (key => {newObject [key] = sourceObject [key];}) ;. Atualize o comentário, se isso fizer sentido.
— kandan
Estou adicionando esta resposta porque nenhuma resposta foi usada Comma operator.
É muito fácil com destructuring assignmente ,operador
Com um certo aprimoramento para a atribuição de propriedades dinâmicas, pode ser assim:
— Code Maniac
meu mau, eu não sei o que eu fiz antes que não fez trabalho, mas parece funcionar
Essa é a melhor expressão, quando ele chegou à desestruturação
— Mohamed Allal
Esta solução é inteligente, mas não funciona no modo estrito (ou seja, 'use strict'). Eu recebo um ReferenceError: a is not defined.
— Kimbaudi 18/07/19
Observe que essa abordagem polui o escopo atual com duas variáveis ae c- tome cuidado para não sobrescrever aleatoriamente vars locais ou globais, dependendo do contexto. (A resposta aceita evita esse problema usando duas variáveis locais em uma função inline, que cai fora do escopo após a execução imediata.)
A poluição do namespace torna isso completamente impraticável. É extremamente comum já ter variáveis ​​no escopo que correspondem às propriedades do objeto; é por isso que existe a abreviação do prop + a desestruturação. Muito provavelmente você terá altura ou cor já definidas, como no exemplo original.
Mais uma solução:
Isso parece muito mais legível para mim do que praticamente qualquer resposta até agora, mas talvez seja apenas eu!
Prefiro ser produtivo do que um código sofisticado, mas confuso, e na engenharia de software da vida real essa é de longe a solução mais legível e sustentável.
Sim, no entanto, para mim, uma vantagem do uso de notação de desestruturação e abreviação é que é menos propenso a erros. Se eu tivesse um centavo por cada vez que copiei e colei erroneamente o código para terminar subset = {color: elmo.color, height: elmo.color}, eu teria pelo menos um ... bem, um centavo talvez.
— JHH 10/01
Eu não chamaria a taquigrafia destruidora de menos propensa a erros, pois não é SECA
— gman 7/04
Eu teria que concordar. Sem poluir o contexto com variáveis ​​indesejadas, essa é de longe a solução mais legível. O resto parece muito confuso. Prefiro entender meu código no segundo em que olho para ele.
Você também pode usar o Lodash .
Complementando, digamos que você tenha uma variedade de "elmo" s:
Se você deseja o mesmo comportamento, usando o lodash, basta:
— Arthur Alvim
A desestruturação em variáveis ​​nomeadas dinamicamente é impossível no JavaScript, conforme discutido nesta pergunta .
Para definir teclas dinamicamente , você pode usar a função reduzir sem alterar o objeto da seguinte maneira:
Observe que você está criando um novo objeto em cada iteração, em vez de atualizar um único clone. - mpen
abaixo está uma versão usando o botão reduzir com clone único (atualizando o valor inicial passado para reduzir).
— Muhammet Enginar
Impressionante! Por um momento, fiquei desconcertado com a importância dos colchetes em [c]:. Eu estou supondo que de alguma forma está causando c ser visto como um valor em vez do nome da propriedade. Enfim, muito legal. 1
— John Fairbanks
Obrigado companheiro! Esse uso é uma coisa que eu amo no JavaScript, que habilita funções genéricas sem usar o eval. Simplesmente, o que ele faz é permitir que você defina uma chave de regra para uma variável em tempo de execução. se você definir var key = 'someKey', poderá usá-lo como {[key]: 'value'}, que fornece {someKey: 'value'}. Realmente legal.
— Muhammet Enginar 15/03/18
Observe que você está criando um novo objeto em cada iteração, em vez de atualizar um único clone.
— mpen 16/05/19
@mpen boa localização. Eu adicionei uma versão que modifica o clone único, como você sugeriu também espalhar args em vez de passar uma matriz de chaves.
Solução TypeScript:
export function pick<T extends object, U extends keyof T>(obj: T, paths: Array<U>) {
return paths.reduce((o, k) => {
As informações de digitação ainda permitem o preenchimento automático:
Crédito para DefinitelyTyped para U extends keyof Ttruque!
Não está trabalhando para mim.
— Nuthinking
@Nuthinking Você está usando o VS Code?
Sim, eu uso o VS Code.
@Nuthinking E você coloca isso em um arquivo .ts? Isso deve funcionar. Eu apenas tentei novamente
Use o pickmétodo da biblioteca lodash se você já estiver usando.
Apenas outra maneira ...
Você pode usar esta função com uma matriz de objetos =)
Isso falharia se o valor da propriedade fosse false(ou falso). jsfiddle.net/nfAs8
— Alxandr
Por isso mudei para keys[i] in obj.
— Elclanrs 22/07/2013
Isso funciona para mim no console do Chrome. Algum problema com isso?
Isso parece legal, mas cria duas variáveis ​​desnecessárias, cor e altura.
— user424174
Não entendo o seu comentário. O requisito do OP era criar um objeto com esses dois elementos #
@MSi Isso não cria apenas um objeto, mas também cria duas variáveis.
— Design by Adrian
Atribuição de reestruturação com propriedades dinâmicas
Esta solução não se aplica apenas ao seu exemplo específico, mas é mais geralmente aplicável:
Você pode definir facilmente subset4etc. para levar mais propriedades em consideração.
Bom e velho Array.prototype.reduce:
esta resposta usa o operador de vírgula mágico, também: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comma_Operator
se você quiser ser realmente chique, isso é mais compacto:
Juntando tudo em uma função reutilizável:
converter argumentos em array
use Array.forEach()para escolher a propriedade
Estender o protótipo de tipos nativos é considerado uma prática ruim, embora funcione. Não faça isso se estiver escrevendo uma biblioteca.
Adicionando meus 2 centavos à resposta de Ivan Nosov :
No meu caso, eu precisava que muitas chaves fossem 'fatiadas' do objeto, para que se tornasse feio muito rápido e não uma solução muito dinâmica:
Então, aqui está uma solução dinâmica usando eval:
Nota: embora a pergunta original feita fosse javascript, isso pode ser feito com o jQuery pela solução abaixo