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.