One-liner per acquisire alcune proprietà dall'oggetto in ES 6


153

Come si può scrivere una funzione, che accetta solo pochi attributi nel modo più compatto in ES6?

Ho trovato una soluzione usando il valore destrutturante + oggetto semplificato letterale, ma non mi piace che l'elenco di campi venga ripetuto nel codice.

Esiste una soluzione ancora più snella?

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

Risposte:


124

Ecco qualcosa di più sottile, anche se non evita di ripetere l'elenco dei campi. Utilizza la "destrutturazione dei parametri" per evitare la necessità del vparametro.

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

(Vedi un esempio eseguibile in questa altra risposta ).

La soluzione di EthanBrown è più generale. Ecco una versione più idiomatica di esso che utilizza Object.assigne proprietà calcolate (la [p]parte):

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

Se vogliamo preservare gli attributi delle proprietà, come configurablee getter e setter, omettendo anche le proprietà non enumerabili, allora:

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

10
+1 bella risposta, torazaburo; grazie per avermi informato Object.assign; es6 è come un albero di Natale con così tanti regali sotto che sto ancora trovando regali mesi dopo le vacanze
— Ethan Brown,

Errore: la descrizione della proprietà deve essere un oggetto: non definito. Non dovrebbe essere filter(...).map(prop => ({[prop]: get(prop)})))?
— Infinito

Per la tua prima pick()implementazione potresti anche fare qualcosa del generereturn props.reduce((r, prop) => (r[prop] = o[prop], r), {})
— Patrick Roberts,

sfortunatamente quella versione di pick non sarà sicura nei flussi o nei dattiloscritti. se si desidera la sicurezza dei tipi, non è possibile in alcun modo distruggere l'assegnazione dell'oggetto originale, quindi assegnare ciascuno a un nuovo oggetto.
— duhseekoh,

Quando una proprietà non esiste in un oggetto, si ottiene undefined. A volte è importante. A parte questo, bella idea.
— X-yuri,

43

Non credo che ci sia modo di renderlo molto più compatto della tua risposta (o di torazburo), ma essenzialmente quello che stai cercando di fare è emulare l' pickoperazione di Underscore . Sarebbe abbastanza facile implementarlo nuovamente in ES6:

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

Quindi hai una comoda funzione riutilizzabile:

var stuff = { name: 'Thing', color: 'blue', age: 17 };
var picked = pick(stuff, 'name', 'age');

Grazie. Questa non è una risposta alla mia domanda, ma un'aggiunta molto bella.
— Kirilloid,

7
(scrollando le spalle) Sento che è una risposta per la tua soluzione; non esiste una soluzione generale più snella (la soluzione di torazaburo rimuove dal verbo extra, ma il problema essenziale - che tutti i nomi delle proprietà devono essere scritti due volte - significa che non si adatta meglio della tua soluzione). La mia soluzione scala almeno bene ... risolve la pickfunzione una volta e puoi scegliere quante proprietà vuoi e non le raddoppierà.
— Ethan Brown,

1
Perché si usa hasOwnProperty? Se i campi sono selezionati a mano, insembra anche essere più appropriato; anche se vorrei omettere completamente il controllo e lasciarlo semplicemente predefinito undefined.
— Bergi,

Bergi, è un punto ragionevole ... Considero solo le proprietà (non i metodi) su una catena di prototipi strane e "puzzolenti" (come in esse sono un odore di codice), e preferisco filtrarle per impostazione predefinita. Se c'è un'applicazione che richiede proprietà prototipo, beh ... ci può essere un'opzione per quello.
— Ethan Brown,

che dire di json array!
— Rizwan Patel,

19

Il trucco per risolverlo come una linea è quello di capovolgere l'approccio adottato: invece di partire dall'oggetto originale orig, si può iniziare dalle chiavi che vogliono estrarre.

Usando Array#reduceuno si può quindi memorizzare ogni chiave necessaria sull'oggetto vuoto che viene passato come initialValueper detta funzione.

Così:

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


11

Una soluzione un po 'più breve usando l'operatore virgola:

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

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


come usare questo? Potete fornire un esempio?
— Tomas M,

1
Funziona proprio come le altre pickfunzioni di questo thread:pick({ name: 'John', age: 29, height: 198 }, 'name', 'age')
— shesek,

8

La proposta relativa alle proprietà di riposo / diffusione dell'oggetto TC39 renderà questo gioco abbastanza fluido:

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

(Ha il rovescio della medaglia di creare le variabili xe yche potrebbe non essere necessario.)


33
Questa è una forma conveniente di omit, ma nonpick
— chirilloide,

5
Mi piacerebbe vedere una variante che fa esattamente il contrario di questo come una proposta ES:let { a, b } as z = { x: 1, y: 2, a: 3, b: 4 }
— gfullam,

3

È possibile utilizzare la distruzione degli oggetti per decomprimere le proprietà dall'oggetto esistente e assegnarle a variabili con nomi diversi : campi di un nuovo oggetto inizialmente vuoto.

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

let newPerson = {};

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

console.log(newPerson);


(indice): 36 Sintassi non
— rilevata

@Remzes non sa dove e come lo stai eseguendo, ma funziona bene nell'editor di codice SO e negli strumenti di sviluppo di Chrome.
— Saksham,

Ho usato jsfiddle
— Remzes

Ho migliorato un po 'la tua risposta, ma è ancora troppo dettagliata, rispetto a quanto richiesto dall'OP. Ripete non solo i nomi dei campi, ma anche il nome del nuovo oggetto.
— Dan Dascalescu,

3

ES6 era l'ultima specifica al momento in cui la domanda è stata scritta. Come spiegato in questa risposta , la selezione dei tasti è significativamente più breve in ES2019 rispetto a ES6:

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

2

Al momento esiste una proposta di Strawman per migliorare la sintassi abbreviata degli oggetti JavaScript, che consentirebbe di "selezionare" le proprietà denominate senza ripetizioni:

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

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

Sfortunatamente, la proposta non sembra andare da nessuna parte presto. Ultima modifica a luglio 2017 e ancora una bozza nella fase 0 , suggerendo che l'autore potrebbe essersi dimenticato o dimenticato.

ES5 e precedenti (modalità non rigorosa)

La stenografia più concisa che mi viene in mente riguarda una caratteristica del linguaggio antico che nessuno usa più:

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

withle dichiarazioni sono vietate in modalità rigorosa, rendendo questo approccio inutile per il 99,999% di JavaScript moderno. Un po 'un peccato, perché questo è l'unico uso a metà decente che ho trovato per la withfunzione. 😀


1

Ho una soluzione simile a quella di Ethan Brown, ma ancora più breve - pickfunzione. Un'altra funzione pick2è un po 'più lunga (e più lenta), ma consente di rinominare le proprietà in modo simile a 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
}, {}) 

Ecco l'esempio di utilizzo:

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

1
Qual è la ragione del downvote? Non funziona per te?
— Alexandr Priezzhev,

0

Ho richiesto questo sollution ma non sapevo se le chiavi proposte fossero disponibili. Quindi, ho preso la risposta di @torazaburo e sono migliorato per il mio caso d'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' }

0

ispirato all'approccio ridotto di https://stackoverflow.com/users/865693/shesek :

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

utilizzo:

pick({ model : 'F40', manufacturer: 'Ferrari', productionYear: 1987 }, 'model', 'productionYear') risulta in: {model: "F40", productionYear: 1987}

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.