Une seule ligne pour prendre certaines propriétés de l'objet dans ES 6

153

Comment écrire une fonction qui ne prend que peu d'attributs de la manière la plus compacte dans ES6?

J'ai trouvé une solution utilisant la déstructuration + un littéral d'objet simplifié, mais je n'aime pas que la liste de champs soit répétée dans le code.

Existe-t-il une solution encore plus mince?

(v) => {
    let { id, title } = v;
    return { id, title };
}
kirilloïde
la source

Réponses:

124

Voici quelque chose de plus mince, même si cela n'évite pas de répéter la liste des champs. Il utilise la «déstructuration des paramètres» pour éviter le besoin du vparamètre.

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

(Voir un exemple exécutable dans cette autre réponse ).

La solution de @ EthanBrown est plus générale. En voici une version plus idiomatique qui utilise Object.assignet calcule des propriétés (la [p]partie):

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

Si nous voulons conserver les attributs des propriétés, tels que configurableet les getters et les setters, tout en omettant également les propriétés non énumérables, alors:

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
la source
10
+1 belle réponse, torazaburo; merci de m'en avoir informé Object.assign; es6 est comme un sapin de Noël avec tant de cadeaux en dessous Je trouve encore des cadeaux des mois après les vacances
Ethan Brown
Vous avez une erreur: la description de la propriété doit être un objet: indéfini. Cela ne devrait-il pas être le cas filter(...).map(prop => ({[prop]: get(prop)})))?
Sans fin
Pour votre première pick()implémentation, vous pouvez également faire quelque chose commereturn props.reduce((r, prop) => (r[prop] = o[prop], r), {})
Patrick Roberts
malheureusement, cette version de pick ne sera pas de type sécurisé dans le flux ou le dactylographie. si vous voulez la sécurité de type, il n'y a aucun moyen de contourner l'affectation de la déstructure de l'objet d'origine, puis de l'attribuer à chacun dans un nouvel objet.
duhseekoh
Lorsqu'une propriété n'existe pas dans un objet, vous obtenez undefined. Parfois ça compte. A part ça, bonne idée.
x-yuri
43

Je ne pense pas qu'il y ait un moyen de le rendre beaucoup plus compact que votre réponse (ou celle de torazburo), mais essentiellement ce que vous essayez de faire est d'imiter le pickfonctionnement de Underscore . Il serait assez facile de réimplémenter cela dans ES6:

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

Ensuite, vous avez une fonction réutilisable pratique:

var stuff = { name: 'Thing', color: 'blue', age: 17 };
var picked = pick(stuff, 'name', 'age');
Ethan Brown
la source
Merci. Ce n'est pas une réponse à ma question, mais un très bel ajout.
kirilloid
7
(haussement d' épaules) Je me sens comme lui est une réponse à votre solution; il n'y a pas de solution générale plus mince (la solution de torazaburo supprime le verbage supplémentaire, mais le problème essentiel - que tous les noms de propriété doivent être écrits deux fois - signifie qu'elle ne s'adapte pas mieux que votre solution). Ma solution évolue au moins bien ... pickune fois la fonction correcte , et vous pouvez choisir autant de propriétés que vous voulez et cela ne les doublera pas.
Ethan Brown le
1
Pourquoi utilisez-vous hasOwnProperty? Si les champs sont sélectionnés à la main, cela insemble même être plus approprié; même si j'irais pour omettre complètement le chèque et les laisser par défaut undefined.
Bergi
Bergi, c'est un point raisonnable ... Je considère juste que les propriétés (pas les méthodes) sur une chaîne de prototypes sont bizarres et "malodorantes" (car elles sont une odeur de code), et je préfère les filtrer par défaut. S'il y a une application qui a besoin de propriétés de prototype, eh bien ... il peut y avoir une option pour cela.
Ethan Brown
qu'en est-il des tableaux Json!
Rizwan Patel le
19

L'astuce pour résoudre ce problème en une seule ligne est d'inverser l'approche adoptée: au lieu de partir de l'objet d'origine orig, on peut partir des clés qu'ils veulent extraire.

En utilisant Array#reduceon peut alors stocker chaque clé nécessaire sur l'objet vide qui est passé en tant que initialValuepour ladite fonction.

Ainsi:

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
la source
11

Une solution un peu plus courte utilisant l'opérateur virgule:

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
la source
comment l'utiliser? Pouvez vous donner un exemple?
Tomas M
1
Cela fonctionne comme les autres pickfonctions de ce fil:pick({ name: 'John', age: 29, height: 198 }, 'name', 'age')
shesek
8

La proposition de propriétés de repos / propagation d'objet de TC39 rendra cette jolie astuce:

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

(Cela présente l'inconvénient de créer les variables xet ydont vous n'avez peut-être pas besoin.)

alxndr
la source
33
C'est une forme pratique de omit, mais paspick
kirilloid
5
let { a, b } as z = { x: 1, y: 2, a: 3, b: 4 }
J'adorerais
3

Vous pouvez utiliser la déstructuration d'objets pour décompresser les propriétés de l'objet existant et les affecter à des variables avec des noms différents - des champs d'un nouvel objet initialement vide.

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

let newPerson = {};

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

console.log(newPerson);

Saksham
la source
(index): 36 Uncaught SyntaxError: Cible d'affectation de déstructuration non valide
Remzes
@Remzes ne sait pas où et comment vous exécutez cela, mais cela fonctionne bien dans l'éditeur de code SO et dans les outils de développement Chrome.
Saksham
J'ai utilisé jsfiddle
Remzes
J'ai un peu amélioré votre réponse, mais elle est encore trop verbeuse par rapport à ce que le PO a demandé. Il répète non seulement les noms des champs, mais également le nom du nouvel objet.
Dan Dascalescu
3

ES6 était la dernière spécification au moment où la question a été écrite. Comme expliqué dans cette réponse , la sélection des clés est nettement plus courte dans ES2019 que dans ES6:

Object.fromEntries(
  Object.entries(obj)
  .filter(([key]) => ['foo', 'bar'].includes(key))
)
Fiole d'Estus
la source
2

Il existe actuellement une proposition de paille pour améliorer la syntaxe abrégée des objets JavaScript, qui permettrait de "sélectionner" les propriétés nommées sans répétition:

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

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

Malheureusement, la proposition ne semble pas aller nulle part de si tôt. Dernière modification en juillet 2017 et toujours un brouillon à l' étape 0 , suggérant que l'auteur l'a peut-être abandonné ou oublié.

ES5 et versions antérieures (mode non strict)

Le raccourci le plus concis possible auquel je puisse penser implique une fonctionnalité de langue ancienne que personne n'utilise plus:

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

withles instructions sont interdites en mode strict, ce qui rend cette approche inutile pour 99,999% du JavaScript moderne. Un peu dommage, car c'est la seule utilisation à moitié décente que j'ai trouvée pour la withfonctionnalité. 😀


la source
1

J'ai similaire à la solution d'Ethan Brown, mais encore plus courte - pickfonction. Une autre fonction pick2est un peu plus longue (et plus lente), mais permet de renommer les propriétés de la même manière que 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
}, {}) 

Voici l'exemple d'utilisation:

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
la source
1
Quelle est la raison du vote négatif? Ça ne marche pas pour toi?
Alexandr Priezzhev
0

J'avais besoin de cette solution mais je ne savais pas si les clés proposées étaient disponibles. Donc, j'ai pris la réponse @torazaburo et je me suis amélioré pour mon cas d'utilisation:

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
la source
0

inspiré de l'approche de réduction de https://stackoverflow.com/users/865693/shesek :

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

usage:

pick({ model : 'F40', manufacturer: 'Ferrari', productionYear: 1987 }, 'model', 'productionYear') résulte en: {model: "F40", productionYear: 1987}

Kevin K.
la source