Uma linha para obter algumas propriedades do objeto no ES 6

153

Como alguém pode escrever uma função, que usa apenas alguns atributos da maneira mais compacta no ES6?

Eu vim com a solução usando destructuring + literal simplificado de objeto, mas não gosto que a lista de campos seja repetida no código.

Existe uma solução ainda mais fina?

(v) => {
    let { id, title } = v;
    return { id, title };
}
kirilloid
fonte

Respostas:

124

Aqui está algo mais simples, embora não evite repetir a lista de campos. Ele usa "destruição de parâmetros" para evitar a necessidade do vparâmetro.

({id, title}) => ({id, title})

(Veja um exemplo executável nesta outra resposta ).

A solução da @ EthanBrown é mais geral. Aqui está uma versão mais idiomática, que usa Object.assigne calcula as propriedades (a [p]parte):

function pick(o, ...props) {
    return Object.assign({}, ...props.map(prop => ({[prop]: o[prop]})));
}

Se queremos preservar os atributos das propriedades, como configurablee getters e setters, além de omitir propriedades não enumeráveis, então:

function pick(o, ...props) {
    var has = p => o.propertyIsEnumerable(p),
        get = p => Object.getOwnPropertyDescriptor(o, p);

    return Object.defineProperties({},
        Object.assign({}, ...props
            .filter(prop => has(prop))
            .map(prop => ({prop: get(props)})))
    );
}
Dan Dascalescu
fonte
10
+1 boa resposta, torazaburo; obrigado por me informar Object.assign; ES6 é como uma árvore de Natal com tantos presentes sob ela ainda estou encontrando presentes meses após o feriado
Ethan Brown
Ocorreu um erro: a descrição da propriedade deve ser um objeto: indefinido. Não deveria ser filter(...).map(prop => ({[prop]: get(prop)})))?
Endless
Para sua primeira pick()implementação, você também pode fazer algo como:return props.reduce((r, prop) => (r[prop] = o[prop], r), {})
Patrick Roberts
infelizmente, essa versão do pick não será do tipo segura no fluxo ou datilografada. se você deseja digitar segurança, não há como desestruturar a atribuição do objeto original e atribuir cada uma a um novo objeto.
precisa saber é o seguinte
Quando uma propriedade não existe em um objeto, você recebe undefined. Às vezes isso importa. Fora isso, boa ideia.
x-yuri
43

Não acho que exista uma maneira de torná-lo muito mais compacto do que sua resposta (ou do torazburo), mas essencialmente o que você está tentando fazer é imitar a pickoperação do Underscore . Seria fácil reimplementar isso no ES6:

function pick(o, ...fields) {
    return fields.reduce((a, x) => {
        if(o.hasOwnProperty(x)) a[x] = o[x];
        return a;
    }, {});
}

Então você tem uma função útil e reutilizável:

var stuff = { name: 'Thing', color: 'blue', age: 17 };
var picked = pick(stuff, 'name', 'age');
Ethan Brown
fonte
Obrigado. Esta não é uma resposta para minha pergunta, mas uma adição muito agradável.
precisa saber é o seguinte
7
(encolhe os ombros) Sinto que é uma resposta para sua solução; não existe uma solução geral mais simples (a solução do torazaburo é removida do excesso de verbage, mas o problema essencial - que todos os nomes de propriedades precisam ser escritos duas vezes - significa que ela não é melhor do que a sua solução). Minha solução pelo menos escala bem ... corrige a pickfunção uma vez, e você pode escolher quantas propriedades quiser e não as duplicará.
Ethan Brown
1
Por que você usa hasOwnProperty? Se os campos são selecionados manualmente, até inparece ser mais apropriado; embora eu optasse por omitir completamente a verificação e apenas deixá-los usar como padrão undefined.
Bergi 24/11/2015
Bergi, é um ponto razoável ... Apenas considero as propriedades (não os métodos) em uma cadeia de protótipos estranhas e "fedorentas" (como são um cheiro de código), e prefiro filtrá-las por padrão. Se há um aplicativo que precisa de propriedades de protótipo, bem ... pode haver uma opção para isso.
Ethan Brown
e quanto às matrizes json!
Rizwan Patel 14/09
19

O truque para resolver isso como uma linha é mudar a abordagem adotada: em vez de começar pelo objeto original orig, pode-se começar pelas chaves que eles desejam extrair.

O uso de Array#reduceum pode armazenar cada chave necessária no objeto vazio que é passado como a initialValuefunção para a referida função.

Igual a:

const orig = {
  id: 123456789,
  name: 'test',
  description: '…',
  url: 'https://…',
};

const filtered = ['id', 'name'].reduce((result, key) => { result[key] = orig[key]; return result; }, {});

console.log(filtered); // Object {id: 123456789, name: "test"}

Bramus
fonte
11

Uma solução um pouco mais curta usando o operador de vírgula:

const pick = (O, ...K) => K.reduce((o, k) => (o[k]=O[k], o), {})

console.log(
  pick({ name: 'John', age: 29, height: 198 }, 'name', 'age')
)  

shesek
fonte
como usar isso? você pode dar um exemplo?
Tomas M
1
Ele funciona exatamente como as outras pickfunções neste tópico:pick({ name: 'John', age: 29, height: 198 }, 'name', 'age')
shesek
8

A proposta de propriedades de descanso / propagação de objetos do TC39 tornará isso muito liso:

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

(Ele tem a desvantagem de criar as variáveis xe yque você pode não precisar.)

alxndr
fonte
33
Esta é uma forma conveniente de omit, mas nãopick
kirilloid
5
Eu adoraria ver uma variante que faz exatamente o oposto disto como uma proposta ES:let { a, b } as z = { x: 1, y: 2, a: 3, b: 4 }
gfullam
3

Você pode usar a desestruturação de objetos para descompactar propriedades do objeto existente e atribuí-las a variáveis ​​com nomes diferentes - campos de um novo objeto inicialmente vazio.

const person = {
  fname: 'tom',
  lname: 'jerry',
  aage: 100,
}

let newPerson = {};

({fname: newPerson.fname, lname: newPerson.lname} = person);

console.log(newPerson);

Saksham
fonte
(index): 36 Untaught SyntaxError: destino de atribuição de desestruturação inválido
Remzes
O @Remzes não sabe onde e como você está executando isso, mas funciona bem no editor de código SO e nas ferramentas de desenvolvedor do Chrome.
Saksham
Eu usei jsfiddle
Remzes
Melhorei um pouco sua resposta, mas ainda é muito detalhada, em comparação com o que o OP solicitava. Ele repete não apenas os nomes dos campos, mas também o nome do novo objeto.
Dan Dascalescu
3

ES6 era a especificação mais recente no momento em que a pergunta foi escrita. Conforme explicado nesta resposta , a seleção de teclas é significativamente menor no ES2019 do que no ES6:

Object.fromEntries(
  Object.entries(obj)
  .filter(([key]) => ['foo', 'bar'].includes(key))
)
Estus Flask
fonte
2

Atualmente, existe uma proposta simplificada para melhorar a sintaxe abreviada de objetos do JavaScript, o que permitiria "escolher" as propriedades nomeadas sem repetição:

const source = {id: "68646", genre: "crime", title: "Scarface"};
const target = {};
Object.assign(target, {source.title, source.id});

console.log(picked);
// {id: "68646", title: "Scarface"}

Infelizmente, a proposta não parece chegar a lugar nenhum tão cedo. Última edição em julho de 2017 e ainda um rascunho no Estágio 0 , sugerindo que o autor tenha se esquecido ou esquecido.

ES5 e versões anteriores (modo não estrito)

A abreviação possível e concisa que posso pensar envolve um recurso de linguagem antiga que ninguém mais usa:

Object.assign(target, {...(o => {
    with(o) return { id, title };
})(source)});

withas instruções são proibidas no modo estrito, tornando essa abordagem inútil para 99,999% do JavaScript moderno. É uma pena, porque esse é o único uso meio decente que encontrei para o withrecurso. 😀


fonte
1

Eu tenho semelhante à solução de Ethan Brown, mas ainda mais curta - pickfunção. Outra função pick2é um pouco mais longa (e mais lenta), mas permite renomear propriedades da maneira semelhante à do ES6.

const pick = (o, ...props) => props.reduce((r, p) => p in o ? {...r, [p]: o[p]} : r, {})

const pick2 = (o, ...props) => props.reduce((r, expr) => {
  const [p, np] = expr.split(":").map( e => e.trim() )
  return p in o ? {...r, [np || p]: o[p]} : r
}, {}) 

Aqui está o exemplo de uso:

const d = { a: "1", c: "2" }

console.log(pick(d, "a", "b", "c"))        // -> { a: "1", c: "2" }
console.log(pick2(d, "a: x", "b: y", "c")) // -> { x: "1", c: "2" }
Alexandr Priezzhev
fonte
1
Qual é o motivo do voto negativo? Isso não funciona para você?
Alexandr Priezzhev
0

Eu exigia essa solução, mas não sabia se as chaves propostas estavam disponíveis. Então, peguei a resposta @torazaburo e melhorei para o meu caso de uso:

function pick(o, ...props) {
  return Object.assign({}, ...props.map(prop => {
    if (o[prop]) return {[prop]: o[prop]};
  }));
}

// Example:
var person = { name: 'John', age: 29 };
var myObj = pick(person, 'name', 'sex'); // { name: 'John' }
Alwin Kesler
fonte
0

inspirado na abordagem de redução de https://stackoverflow.com/users/865693/shesek :

const pick = (orig, ...keys) => keys.reduce((acc, key) => ({...acc, [key]: orig[key]}), {})

uso:

pick({ model : 'F40', manufacturer: 'Ferrari', productionYear: 1987 }, 'model', 'productionYear') resulta em: {model: "F40", productionYear: 1987}

Kevin K.
fonte