Einzeiler, um einige Eigenschaften aus dem Objekt in ES 6 zu übernehmen

153

Wie kann man eine Funktion schreiben, die in ES6 nur wenige Attribute auf kompakteste Weise akzeptiert?

Ich habe eine Lösung mit Destrukturierung + vereinfachtem Objektliteral gefunden, aber ich mag es nicht, dass die Liste der Felder im Code wiederholt wird.

Gibt es eine noch schlankere Lösung?

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

Antworten:

124

Hier ist etwas Schlankeres, obwohl es nicht vermieden wird, die Liste der Felder zu wiederholen. Es verwendet "Parameter-Destrukturierung", um die Notwendigkeit des vParameters zu vermeiden .

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

(Siehe ein ausführbares Beispiel in dieser anderen Antwort ).

Die Lösung von @ EthanBrown ist allgemeiner. Hier ist eine idiomatischere Version davon Object.assign, die Eigenschaften (den [p]Teil) verwendet und berechnet :

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

Wenn wir die Attribute der Eigenschaften wie z. B. configurableGetter und Setter beibehalten und gleichzeitig nicht aufzählbare Eigenschaften weglassen möchten , dann:

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
quelle
10
+1 nette Antwort, Torazaburo; danke, dass du mich darauf aufmerksam gemacht hast Object.assign; es6 ist wie ein Weihnachtsbaum mit so vielen Geschenken darunter, dass ich noch Monate nach dem Feiertag Geschenke finde
Ethan Brown
Fehlermeldung: Die Eigenschaftsbeschreibung muss ein Objekt sein: undefiniert. Sollte es nicht sein filter(...).map(prop => ({[prop]: get(prop)})))?
Endlos
Für Ihre erste pick()Implementierung könnten Sie auch so etwas tunreturn props.reduce((r, prop) => (r[prop] = o[prop], r), {})
Patrick Roberts
Leider ist diese Version von pick in Flow oder Typoskript nicht typsicher. Wenn Sie Typensicherheit wünschen, führt kein Weg daran vorbei, die Zuweisung des ursprünglichen Objekts zu zerstören und jedes Objekt einem neuen Objekt zuzuweisen.
Duhseekoh
Wenn eine Eigenschaft in einem Objekt nicht vorhanden ist, erhalten Sie undefined. Manchmal ist es wichtig. Ansonsten schöne Idee.
X-Yuri
43

Ich glaube nicht, dass es eine Möglichkeit gibt, es viel kompakter zu gestalten als Ihre Antwort (oder die von Torazburo), aber im Wesentlichen versuchen Sie, die pickOperation von Underscore zu emulieren . Es wäre einfach genug, dies in ES6 erneut zu implementieren:

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

Dann haben Sie eine praktische wiederverwendbare Funktion:

var stuff = { name: 'Thing', color: 'blue', age: 17 };
var picked = pick(stuff, 'name', 'age');
Ethan Brown
quelle
Vielen Dank. Dies ist keine Antwort auf meine Frage, aber eine sehr schöne Ergänzung.
Kirilloid
7
(Achselzucken) Ich denke, es ist eine Antwort auf Ihre Lösung. Es gibt keine schlankere allgemeine Lösung (die Lösung von torazaburo entfernt zusätzliche Verben, aber das wesentliche Problem - dass alle Eigenschaftsnamen zweimal geschrieben werden müssen - bedeutet, dass sie nicht besser skaliert als Ihre Lösung). Meine Lösung lässt sich zumindest gut skalieren ... Richten Sie die pickFunktion einmal aus, und Sie können so viele Eigenschaften auswählen, wie Sie möchten, und sie werden nicht verdoppelt.
Ethan Brown
1
Warum benutzt du hasOwnProperty? Wenn die Felder von Hand ausgewählt sind, inscheint dies sogar angemessener zu sein. obwohl ich mich dafür entscheiden würde, die Prüfung komplett wegzulassen und sie einfach auf Standard setzen zu lassen undefined.
Bergi
Bergi, es ist ein vernünftiger Punkt ... Ich betrachte Eigenschaften (nicht Methoden) einer Prototypenkette nur als seltsam und "stinkend" (da es sich um einen Codegeruch handelt), und ich ziehe es vor, sie standardmäßig herauszufiltern. Wenn es eine Anwendung gibt, die Prototyp-Eigenschaften benötigt, kann es eine Option dafür geben.
Ethan Brown
Was ist mit JSON-Arrays?
Rizwan Patel
19

Der Trick, um dies als Einzeiler zu lösen, besteht darin, den gewählten Ansatz umzudrehen: Anstatt vom ursprünglichen Objekt auszugehen orig, kann man von den Schlüsseln ausgehen, die extrahiert werden sollen.

Mit Array#reduceone kann dann jeder benötigte Schlüssel auf dem leeren Objekt gespeichert werden, das als initialValuefür diese Funktion übergeben wird.

Wie so:

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
quelle
11

Eine etwas kürzere Lösung mit dem Komma-Operator:

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
quelle
Wie benutzt man das? Können Sie ein Beispiel geben?
Tomas M
1
Es funktioniert genau wie die anderen pickFunktionen in diesem Thread:pick({ name: 'John', age: 29, height: 198 }, 'name', 'age')
Shesek
8

Der Vorschlag für Objektrest- / Spreizungseigenschaften von TC39 macht dies ziemlich raffiniert:

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

(Es hat den Nachteil, die xund yVariablen zu erstellen , die Sie möglicherweise nicht benötigen.)

alxndr
quelle
33
Dies ist eine bequeme Form von omit, aber nichtpick
Kirilloid
5
Ich würde gerne eine Variante sehen, die genau das Gegenteil davon als ES-Vorschlag macht:let { a, b } as z = { x: 1, y: 2, a: 3, b: 4 }
Gfullam
3

Mithilfe der Objektdestrukturierung können Sie Eigenschaften aus dem vorhandenen Objekt entpacken und sie Variablen mit unterschiedlichen Namen zuweisen - Felder eines neuen, anfangs leeren Objekts.

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

let newPerson = {};

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

console.log(newPerson);

Saksham
quelle
(Index): 36 Ungefangener Syntaxfehler: Ungültiges Ziel für die Destrukturierungszuweisung
Remzes
@Remzes weiß nicht, wo und wie Sie dies ausführen, aber es funktioniert gut im SO-Code-Editor und in Chrome-Entwicklertools.
Saksham
Ich habe jsfiddle
Remzes
Ich habe Ihre Antwort ein wenig verbessert, aber sie ist immer noch zu ausführlich im Vergleich zu dem, was das OP verlangt hat. Es werden nicht nur die Feldnamen, sondern auch der Name des neuen Objekts wiederholt.
Dan Dascalescu
2

Derzeit gibt es einen Strohmann-Vorschlag zur Verbesserung der JavaScript-Kurzschrift-Syntax, mit der benannte Eigenschaften ohne Wiederholung "ausgewählt" werden können:

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

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

Leider scheint der Vorschlag nicht in absehbarer Zeit irgendwohin zu gehen. Zuletzt bearbeitet im Juli 2017 und immer noch ein Entwurf in Phase 0 , was darauf hindeutet, dass der Autor ihn möglicherweise fallen gelassen oder vergessen hat.

ES5 und früher (nicht strenger Modus)

Die prägnanteste Abkürzung, die ich mir vorstellen kann, beinhaltet eine alte Sprachfunktion, die niemand mehr verwendet:

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

withAnweisungen sind im strengen Modus verboten, sodass dieser Ansatz für 99,999% des modernen JavaScript unbrauchbar ist. Ein bisschen schade, denn dies ist die einzige halbwegs anständige Verwendung, die ich für diese withFunktion gefunden habe. 😀


quelle
2

ES6 war die neueste Spezifikation zu dem Zeitpunkt, als die Frage geschrieben wurde. Wie in dieser Antwort erläutert , ist die Schlüsselauswahl in ES2019 erheblich kürzer als in ES6:

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

Ich habe eine ähnliche Lösung wie Ethan Brown, aber eine noch kürzere pickFunktion. Eine andere Funktion pick2ist etwas länger (und langsamer), ermöglicht jedoch das Umbenennen von Eigenschaften ähnlich wie bei 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
}, {}) 

Hier ist das Anwendungsbeispiel:

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
quelle
1
Was ist der Grund für eine Ablehnung? Funktioniert es nicht für dich?
Alexandr Priezzhev
0

Ich benötigte diese Lösung, wusste aber nicht, ob die vorgeschlagenen Schlüssel verfügbar waren. Also nahm ich die Antwort von @torazaburo und verbesserte mich für meinen Anwendungsfall:

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
quelle
0

inspiriert vom Reduktionsansatz von https://stackoverflow.com/users/865693/shesek :

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

Verwendung:

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

Kevin K.
quelle