768 Stimmen

Wie man eine Untermenge der Eigenschaften eines JavaScript-Objekts erhält

Sagen wir, ich habe ein Objekt:

elmo = { 
  color: 'rot',
  annoying: true,
  height: 'unbekannt',
  meta: { one: '1', two: '2'}
};

Ich möchte ein neues Objekt mit einem Teil seiner Eigenschaften erstellen.

 // Pseudocode
 subset = elmo.slice('color', 'height')

 //=> { color: 'rot', height: 'unbekannt' }

Wie kann ich das erreichen?

1161voto

Ivan Nosov Punkte 13551

Verwendung von Object-Destrukturierung und Property-Shorthand

const object = { a: 5, b: 6, c: 7  };
const picked = (({ a, c }) => ({ a, c }))(object);

console.log(picked); // { a: 5, c: 7 }

Von Philipp Kewisch:

Dies ist wirklich nur eine sofort aufgerufene anonyme Funktion. Dies alles ist auf der Seite zur Destrukturierungs-Zuweisung auf MDN zu finden. Hier ist eine erweiterte Form

let unwrap = ({a, c}) => ({a, c});

let unwrap2 = function({a, c}) { return { a, c }; };

let picked = unwrap({ a: 5, b: 6, c: 7 });

let picked2 = unwrap2({a: 5, b: 6, c: 7})

console.log(picked)
console.log(picked2)

335voto

Estus Flask Punkte 176276

Zwei gängige Ansätze sind Destrukturierung und konventionelle Lodash-ähnliche pick/omit Implementierung. Der wichtigste praktische Unterschied zwischen ihnen besteht darin, dass Destrukturierung eine Liste von Schlüsseln erfordert, die statisch sein müssen, nicht weggelassen werden können, beinhaltet nicht existierende ausgewählte Schlüssel, d.h. sie ist inklusiv. Dies kann wünschenswert sein oder auch nicht, und kann für die Destrukturierungssyntax nicht geändert werden.

Gegeben:

var obj = { 'foo-bar': 1, bar: 2, qux: 3 };

Das erwartete Ergebnis für das reguläre Auswählen der Schlüssel foo-bar, bar, baz lautet:

{ 'foo-bar': 1, bar: 2 }

Das erwartete Ergebnis für das inklusive Auswählen lautet:

{ 'foo-bar': 1, bar: 2, baz: undefined }

Destrukturierung

Die Destrukturierungssyntax ermöglicht es, ein Objekt zu zerlegen und neu zu kombinieren, entweder mit Funktionsparametern oder Variablen.

Die Einschränkung besteht darin, dass eine Liste von Schlüsseln vordefiniert ist, sie können nicht als Zeichenfolgen aufgelistet werden, wie in der Frage beschrieben. Destrukturierung wird komplizierter, wenn ein Schlüssel nicht alphanumerisch ist, z.B. foo-bar.

Der Vorteil ist, dass es eine performante Lösung ist, die nativ in ES6 ist.

Der Nachteil ist, dass eine Liste von Schlüsseln dupliziert wird, was zu einem umständlichen Code führt, wenn die Liste lang ist. Da die Destrukturierung in diesem Fall das Objektliteral dupliziert, kann die Liste einfach kopiert und eingefügt werden.

IIFE

const subset = (({ 'foo-bar': foo, bar, baz }) => ({ 'foo-bar': foo, bar, baz }))(obj);

Temporäre Variablen

Kann zur Kollision von Variablennamen im aktuellen Bereich führen:

const { 'foo-bar': foo, bar, baz } = obj;
const subset = { 'foo-bar': foo, bar, baz };

Blockebene kann verwendet werden, um dies zu vermeiden:

let subset;
{
  const { 'foo-bar': foo, bar, baz } = obj;
  subset = { 'foo-bar': foo, bar, baz };
}

Eine Liste von Zeichenfolgen

Eine beliebige Liste von ausgewählten Schlüsseln besteht aus Zeichenfolgen, wie in der Frage gefordert. Dies ermöglicht es, sie nicht vordefinieren zu müssen und Variablen zu verwenden, die Schlüsselnamen enthalten, ['foo-bar', someKey, ...moreKeys].

ECMAScript 2017 hat Object.entries und Array.prototype.includes, ECMAScript 2019 hat Object.fromEntries, sie können bei Bedarf polyfilled werden.

Einzeiler

Angesichts der Tatsache, dass ein Objekt zum Auswählen zusätzliche Schlüssel enthält, ist es im Allgemeinen effizienter, über Schlüssel aus einer Liste zu iterieren als über Objektschlüssel, und umgekehrt, wenn Schlüssel ausgelassen werden müssen.

Pick (ES5)

var subset = ['foo-bar', 'bar', 'baz']
.reduce(function (obj2, key) {
  if (key in obj) // Zeile kann entfernt werden, um sie inklusiv zu machen
    obj2[key] = obj[key];
  return obj2;
}, {});

Omit (ES5)

var subset = Object.keys(obj)
.filter(function (key) { 
  return ['baz', 'qux'].indexOf(key) < 0;
})
.reduce(function (obj2, key) {
  obj2[key] = obj[key];
  return obj2;
}, {});

Pick (ES6)

const subset = ['foo-bar', 'bar', 'baz']
.filter(key => key in obj) // Zeile kann entfernt werden, um sie inklusiv zu machen
.reduce((obj2, key) => (obj2[key] = obj[key], obj2), {});

Omit (ES6)

const subset = Object.keys(obj)
.filter(key => ['baz', 'qux'].indexOf(key) < 0)
.reduce((obj2, key) => (obj2[key] = obj[key], obj2), {});

Pick (ES2019)

const subset = Object.fromEntries(
  ['foo-bar', 'bar', 'baz']
  .filter(key => key in obj) // Zeile kann entfernt werden, um sie inklusiv zu machen
  .map(key => [key, obj[key]])
);

Omit (ES2019)

const subset = Object.fromEntries(
  Object.entries(obj)
  .filter(([key]) => !['baz', 'qux'].includes(key))
);

Wiederverwendbare Funktionen

Einzeiler können als wiederverwendbare Hilfsfunktionen dargestellt werden, ähnlich wie Lodash pick oder omit, wobei eine Liste von Schlüsseln über Argumente übergeben wird, pick(obj, 'foo-bar', 'bar', 'baz').

JavaScript

const pick = (obj, ...keys) => Object.fromEntries(
  keys
  .filter(key => key in obj)
  .map(key => [key, obj[key]])
);

const inclusivePick = (obj, ...keys) => Object.fromEntries(
  keys.map(key => [key, obj[key]])
);

const omit = (obj, ...keys) => Object.fromEntries(
  Object.entries(obj)
  .filter(([key]) => !keys.includes(key))
);

TypeScript

Gutschrift geht an @Claude.

const pick = (obj: T, ...keys: K[]) => (
  Object.fromEntries(
    keys
    .filter(key => key in obj)
    .map(key => [key, obj[key]])
  ) as Pick
);

const inclusivePick = (
  obj: T, ...keys: K[]
) => (
  Object.fromEntries(
    keys
    .map(key => [key, obj[key as unknown as keyof T]])
  ) as {[key in K]: key extends keyof T ? T[key] : undefined}
)

const omit = (
  obj: T, ...keys: K[]
) =>(
  Object.fromEntries(
    Object.entries(obj)
    .filter(([key]) => !keys.includes(key as K))
  ) as Omit
)

255voto

Ygg Punkte 3546

Ich schlage vor, einen Blick auf Lodash zu werfen; es hat viele großartige Hilfsfunktionen.

Zum Beispiel würde pick() genau das sein, wonach Sie suchen:

var subset = _.pick(elmo, ['color', 'height']);

fiddle

198voto

Lauren Punkte 2557

Wenn Sie ES6 verwenden, gibt es einen sehr prägnanten Weg, dies mit Destrukturierung zu tun. Destrukturierung ermöglicht es Ihnen, Objekten leicht etwas hinzuzufügen, indem Sie ein Spread verwenden, aber es ermöglicht Ihnen auch, Teilmengen von Objekten auf die gleiche Weise zu erstellen.

const object = {
  a: 'a',
  b: 'b',
  c: 'c',
  d: 'd',
}

// Entfernen Sie die Felder "c" und "d" aus dem Originalobjekt:
const {c, d, ...partialObject} = object;
const subset = {c, d};

console.log(partialObject) // => { a: 'a', b: 'b'}
console.log(subset) // => { c: 'c', d: 'd'};

112voto

Josh from Qaribou Punkte 6466

Während es etwas ausführlicher ist, können Sie das, was alle anderen vor 2 Jahren empfohlen haben, nämlich underscore/lodash, mit Array.prototype.reduce erreichen.

var subset = ['color', 'height'].reduce(function(o, k) { o[k] = elmo[k]; return o; }, {});

Dieser Ansatz löst es von der anderen Seite: Anstatt ein Objekt zu nehmen und Eigenschaftsnamen darin zu übergeben, um sie zu extrahieren, nehmen Sie ein Array von Eigenschaftsnamen und reduzieren sie in ein neues Objekt.

Obwohl es im einfachsten Fall ausführlicher ist, ist ein Callback hier ziemlich praktisch, da Sie leicht einige gemeinsame Anforderungen erfüllen können, z. B. die 'color'-Eigenschaft in 'colour' im neuen Objekt ändern, Arrays zusammenführen usw. — alles, was Sie tun müssen, wenn Sie ein Objekt von einem Dienst/Bibliothek erhalten und ein neues Objekt erstellen, das an anderer Stelle benötigt wird. Während underscore/lodash ausgezeichnete, gut implementierte Bibliotheken sind, ist dies mein bevorzugter Ansatz für weniger Abhängigkeit von Anbietern und einen einfacheren, konsistenteren Ansatz, wenn meine Logik für die Untergruppenbildung komplexer wird.

Bearbeitung: ES7-Version des gleichen:

const subset = ['color', 'height'].reduce((a, e) => (a[e] = elmo[e], a), {});

Bearbeitung: Ein schönes Beispiel für Currying! Lassen Sie eine 'pick'-Funktion eine andere Funktion zurückgeben.

const pick = (...props) => o => props.reduce((a, e) => ({ ...a, [e]: o[e] }), {});

Das obige ist ziemlich nah an der anderen Methode, außer dass es Ihnen ermöglicht, einen 'Picker' auf die schnelle zu erstellen, z. B.

pick('color', 'height')(elmo);

Was besonders raffiniert an diesem Ansatz ist, ist, dass Sie die ausgewählten 'Picks' problemlos an alles weitergeben können, was eine Funktion benötigt, z. B. Array#map:

[elmo, grover, bigBird].map(pick('color', 'height'));
// [
//   { color: 'rot', height: 'kurz' },
//   { color: 'blau', height: 'mittel' },
//   { color: 'gelb', height: 'groß' },
// ]

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X