Wie kann man in Javascript einem Objekt bedingt ein Mitglied hinzufügen?

386

Ich möchte ein Objekt mit einem bedingt hinzugefügten Mitglied erstellen. Der einfache Ansatz ist:

var a = {};
if (someCondition)
    a.b = 5;

Jetzt möchte ich einen idiomatischeren Code schreiben. Ich versuche:

a = {
    b: (someCondition? 5 : undefined)
};

Aber jetzt bist ein Mitglied, adessen Wert ist undefined. Dies ist nicht das gewünschte Ergebnis.

Gibt es eine praktische Lösung?

Aktualisieren

Ich suche nach einer Lösung, die den allgemeinen Fall mit mehreren Mitgliedern behandeln kann.

a = {
  b: (conditionB? 5 : undefined),
  c: (conditionC? 5 : undefined),
  d: (conditionD? 5 : undefined),
  e: (conditionE? 5 : undefined),
  f: (conditionF? 5 : undefined),
  g: (conditionG? 5 : undefined),
 };
viebel
quelle
23
Ja da ist; das erste Stück Code.
Paolo Bergantino
6
Ich bin mir nicht sicher, ob es so etwas wie idiomatisches JavaScript gibt ...
Michael Berkowski
Ist das eigentlich wichtig? Wenn Sie nie definiert haben a.b, a.bwürde das Abrufen trotzdem zurückkehren undefined.
Teemu
6
@Teemu: Es kann wichtig sein, wann der inOperator verwendet wird.
1
Es gibt derzeit keine Möglichkeit, bedingte Eigenschaften in Literalobjekten zu haben, aber ich wünschte, sie würden sie in ES7 hinzufügen. Dies könnte besonders bei der serverseitigen Programmierung sehr praktisch sein!
Ali

Antworten:

118

In reinem Javascript kann ich mir nichts Idiomatischeres vorstellen als Ihr erstes Code-Snippet.

Wenn die Verwendung der jQuery-Bibliothek jedoch nicht ausgeschlossen ist, sollte $ .extend () Ihren Anforderungen entsprechen, da in der Dokumentation Folgendes angegeben ist :

Undefinierte Eigenschaften werden nicht kopiert.

Daher können Sie schreiben:

var a = $.extend({}, {
    b: conditionB ? 5 : undefined,
    c: conditionC ? 5 : undefined,
    // and so on...
});

Und erhalten Sie die Ergebnisse, die Sie erwarten (wenn conditionBja false, dann bwird es in nicht existieren a).

Frédéric Hamidi
quelle
21
Es gibt eine viel bessere Methode, für die jQuery nicht erforderlich ist. Schauen Sie sich die Antwort von Jamie Hill an.
Andrew Rasmussen
13
@ Andrew, diese Antwort erfordert ES6, das zum Zeitpunkt meines Schreibens noch nicht vorhanden war.
Frédéric Hamidi
funktioniert null genauso? oder muss es undefiniert sein?
Aous1000
Dies ist eigentlich eine falsche Antwort, da jQuery verwendet wird und diese ternäre Bedingung keine Eigenschaft aus einem Objekt entfernt. Dies würde lediglich eine Eigenschaft als undefiniert festlegen. Siehe @lagistos Antwort für den richtigen Weg, dies zu tun,
Alexander Kim
Ja, sehen Sie sich Jamies Antwort unten an: stackoverflow.com/a/40560953/10222449
MadMac
868

Ich denke, @InspiredJW hat es mit ES5 gemacht, und wie @trincot betonte, ist die Verwendung von es6 ein besserer Ansatz. Mit dem Spread-Operator und der logischen UND-Kurzschlussbewertung können wir jedoch etwas mehr Zucker hinzufügen:

const a = {
   ...(someCondition && {b: 5})
}
Jamie Hill
quelle
2
Ich bin mir nicht so sicher, ob dies richtig ist, heißt es in dem VorschlagNull/Undefined Are Ignored , es heißt nicht, dass falsees ignoriert wird. Transpiler können dies derzeit zulassen, aber ist es konform? Das Folgende sollte sein {...someCondition ? {b: 5} : null}, ist aber nicht so kompakt.
Benjamin Dobell
23
Ich fragte, ob dies für die Leute gültig sei, die einen Verbreitungsvorschlag gemacht hatten, und sie sagten, dies sei in Ordnung. github.com/tc39/proposal-object-rest-spread/issues/45 , cc @BenjaminDobell
김민준
73
Der @ AlanH-Spread-Operator ist wie eine Abkürzung von Object.assignund hat eine geringere Priorität als der && -Operator. Der Wert ohne Eigenschaft (boolean, null, undefined, number) wird ignoriert und alle Eigenschaften des Objekts nach dem ...In-Place hinzugefügt . Denken Sie daran, dass der &&Operator den richtigen Wert zurückgibt, wenn er wahr ist, oder andernfalls falsch. also , wenn someConditionwahr ist, {b : 5}wird an den weitergegeben wird ...Operator, was das Hinzufügen der Eigenschaft bzu amit Wert 5. ist someConditionfalsch, falsewird an den ...Bediener übergeben. was zu nichts hinzugefügt. es ist klug. Ich liebe es.
Félix Brunet
11
Gute Antwort, aber wenn Sie die Bedingung und das resultierende Objekt in Klammern setzen, wird die Lesbarkeit dieses Beispiels erheblich verbessert. Nicht jeder erinnert sich auswendig an die Priorität des JS-Operators.
Neurotransmitter
6
Das einzige andere Problem ist, dass Sie dies nicht für falsche Boolesche Werte verwenden können.
ggb667
92

Mit EcmaScript2015 können Sie Folgendes verwenden Object.assign:

Object.assign(a, conditionB ? { b: 1 } : null,
                 conditionC ? { c: 2 } : null,
                 conditionD ? { d: 3 } : null);

Einige Anmerkungen:

Noch prägnanter

Unter dem zweiten Punkt weiter, könnte man es verkürzt wie folgt (wie @Jamie bemerkt hat), als falsy Werte keine eigenen enumerable Eigenschaften ( false, 0, NaN, null, undefined, '', mit Ausnahme document.all):

Object.assign(a, conditionB && { b: 1 },
                 conditionC && { c: 2 },
                 conditionD && { d: 3 });

Trincot
quelle
1
Ich bevorzuge die anfängliche Lösung: einfacher herauszufinden, was los ist - ich wäre mir nicht sicher, was mit einem primitiven Wert passiert (zumindest nicht ohne die Spezifikation zu betrachten).
Axel Rauschmayer
1
Ich liebe die Abkürzung, die die ternäre Logik vereinfacht. Genau das brauchte ich. Vielen Dank!
theUtherSide
80
const obj = {
   ...(condition) && {someprop: propvalue},
   ...otherprops
}

Live-Demo:

const obj = {
  ...(true) && {someprop: 42},
  ...(false) && {nonprop: "foo"},
  ...({}) && {tricky: "hello"},
}

console.log(obj);

Lagistos
quelle
7
Während dieses Code-Snippet die Frage lösen kann, hilft eine Erklärung wirklich, die Qualität Ihres Beitrags zu verbessern. Denken Sie daran, dass Sie die Frage für Leser in Zukunft beantworten und diese Personen möglicherweise die Gründe für Ihren Codevorschlag nicht kennen.
Ralf Stubner
1
Was trägt diese Antwort zu Jamie Hills Antwort von vor 2 Jahren bei ?
Dan Dascalescu
Wenn cond nicht übereinstimmt, wird undefined zurückgegeben.
Mustkeem K
2
Dies funktioniert und es hat eine schönere Syntax als alle anderen Antworten IMO.
Seph Reed
1
Die kurze Erklärung lautet wie folgt: "..." Der Spread-Operator dekonstruiert das Objektliteral und fügt es zu "obj" hinzu, z. B. in diesem Fall ... (true) && {someprop: 42}, der gesamte zu dekonstruierende Begriff ist "(true) && {someprop: 42}", in diesem Fall ist der Boolesche Wert true und der Term ergibt nur {someprop: 42}, das dann dekonstruiert und in obj hinzugefügt wird. Wenn der Boolesche Wert stattdessen falsch ist, ist der Begriff einfach falsch, und nichts wird dekonstruiert und in obj
Qiong Wu
50

Die Verwendung der Spread-Syntax mit Booleschen Werten (wie hier vorgeschlagen) ist keine gültige Syntax. Spread kann nur mit iterables verwendet werden .

Ich schlage folgendes vor:

const a = {
   ...(someCondition? {b: 5}: {} )
}
Itai Noam
quelle
2
@HossamMourad sollte es nicht, da der vorgeschlagene Code bereits in einer Antwort verwendet wurde, die vor mehr als 2 Jahren veröffentlicht wurde. Und die erste Bemerkung ist falsch. Dies ist eine gültige Syntax:{...false}
Trincot
1
@Itai, das ist nicht wahr; Primitive Werte werden umbrochen, wenn sie mit der Spread-Syntax verwendet werden. {...false}ist eine gültige Syntax.
Trincot
@trincot, können Sie bitte Referenz angeben?
Itai Noam
3
Die EcmaScript 2018-Sprachspezifikation, Abschnitt 12.2.6 (.8) gibt an, dass CopyDataPropertiesder Wert ( falsein diesem Fall) ausgeführt wird. Dies beinhaltet das Boxen eines Grundelements (Abschnitte 7.3.23, 7.1.13). Der Link, den Sie zu MDN haben, erwähnt diese "Ausnahme" in Klammern.
Trincot
Als Referenz siehe auch diesen Beitrag, in dem ich auf dasselbe Thema eingehe.
Trincot
26

Wie wäre es mit der Verwendung erweiterter Objekteigenschaften und der Festlegung der Eigenschaft nur, wenn sie wahr ist, z.

[isConditionTrue() && 'propertyName']: 'propertyValue'

Wenn die Bedingung nicht erfüllt ist, wird die bevorzugte Eigenschaft nicht erstellt, und Sie können sie verwerfen. Siehe: http://es6-features.org/#ComputedPropertyNames

UPDATE: Es ist noch besser, dem Ansatz von Axel Rauschmayer in seinem Blog-Artikel über das bedingte Hinzufügen von Einträgen in Objektliteralen und Arrays zu folgen ( http://2ality.com/2017/04/conditional-literal-entries.html ):

const arr = [
  ...(isConditionTrue() ? [{
    key: 'value'
  }] : [])
];

const obj = {
  ...(isConditionTrue() ? {key: 'value'} : {})
};

Hat mir sehr geholfen.

Dimitri Reifschneider
quelle
1
Es wird fast funktionieren. Das Problem ist, dass ein zusätzlicher falseSchlüssel hinzugefügt wird . Zum Beispiel {[true && 'a']: 17, [false && 'b']: 42}ist{a:17, false: 42}
viebel
3
Ich fand einen prägnanteren Weg: ...isConditionTrue() && { propertyName: 'propertyValue' }
Dimitri Reifschneider
Besserer Weg: ... (isConditionTrue ()? {Key: 'value'}: {})
Dimitri Reifschneider
Der Axel Rauschmayer Blog Link gibt diese Antwort. Das Beispiel "... insertIf (cond, 'a')" im Artikel ist genau das, wonach ich gesucht habe. Vielen Dank
Joseph Simpson
20

vereinfacht,

const a = {
    ...(condition && {b: 1}) // if condition is true 'b' will be added.
}
Faheel Khan
quelle
5

Wenn das Ziel darin besteht, dass das Objekt in sich geschlossen erscheint und sich in einer Reihe von Klammern befindet, können Sie Folgendes versuchen:

var a = new function () {
    if (conditionB)
        this.b = 5;

    if (conditionC)
        this.c = 5;

    if (conditionD)
        this.d = 5;
};
Casey Chu
quelle
5

Wenn Sie diese Serverseite (ohne JQuery) ausführen möchten, können Sie lodash 4.3.0 verwenden:

a = _.pickBy({ b: (someCondition? 5 : undefined) }, _.negate(_.isUndefined));

Und das funktioniert mit lodash 3.10.1

a = _.pick({ b: (someCondition? 5 : undefined) }, _.negate(_.isUndefined));
Mickl
quelle
Keine Notwendigkeit für Lodash in ES6.
Dan Dascalescu
5
var a = {
    ...(condition ? {b: 1} : '') // if condition is true 'b' will be added.
}

Ich hoffe, dies ist der viel effizientere Weg, um einen Eintrag basierend auf der Bedingung hinzuzufügen. Weitere Informationen zum bedingten Hinzufügen von Einträgen in Objektliteralen.

Madhankumar
quelle
3
[...condition?'':['item']]Dies wird String-Element in Array hinzufügen
Wayou
Wie ist diese Antwort besser als die Antwort von Jamie Hill aus einem Jahr zuvor ?
Dan Dascalescu
1
@DanDascalescu Jamie Hills Antwort ist besser als meine Antwort. Ich habe nicht so gedacht und war früher eher ein Typ mit ternären Operatoren.
Madhankumar
4

Dies wurde lange beantwortet, aber als ich mir andere Ideen ansah, kam ich auf eine interessante Ableitung:

Weisen Sie derselben Eigenschaft undefinierte Werte zu und löschen Sie sie anschließend

Erstellen Sie Ihr Objekt mit einem anonymen Konstruktor und weisen Sie immer undefinierte Elemente demselben Dummy- Element zu, das Sie ganz am Ende entfernen. Dies gibt Ihnen eine einzelne Zeile (hoffentlich nicht zu komplex) pro Mitglied + 1 zusätzliche Zeile am Ende.

var a = new function() {
    this.AlwaysPresent = 1;
    this[conditionA ? "a" : "undef"] = valueA;
    this[conditionB ? "b" : "undef"] = valueB;
    this[conditionC ? "c" : "undef"] = valueC;
    this[conditionD ? "d" : "undef"] = valueD;
    ...
    delete this.undef;
};
Robert Koritnik
quelle
4

Sie können alle Ihre undefinierten Werte ohne Bedingung hinzufügen und dann verwenden JSON.stringify, um sie alle zu entfernen:

const person = {
  name: undefined,
  age: 22,
  height: null
}

const cleaned = JSON.parse(JSON.stringify(person));

// Contents of cleaned:

// cleaned = {
//   age: 22,
//   height: null
// }
Milad
quelle
2

Ich würde das tun

var a = someCondition ? { b: 5 } : {};

Mit einer Zeilencode-Version bearbeitet

jwchang
quelle
Wenn die Bedingung falsch ist, ist a nackt definiert, was nicht korrekt ist.
Bingjie2680
Es @ bingjie2680 ist nicht so klar , wie es eine sein sollte , wenn someCondition falsch ist. Ich habe nur angenommen a = undefined. Es kann leicht geändert werden mit return { b: undefined };: /
jwchang
@ bingjie2680 Dann sollte es sein , return {};in elseTeil
jwchang
1
Das würdest du nicht wirklich tun ... oder? Ich meine, selbst wenn Sie die gesamte Literal-Syntax wie bisher bedingt machen würden, warum würden Sie den bedingten Operator nicht verwenden? a = condition ? {b:5} : undefined;
3
Herzlichen Glückwunsch, Sie haben gerade drei absolut einfache Zeilen in eine 7-Zeilen-Funktion ohne Verstärkung umgewandelt. Nein, die Verwendung einer anonymen Funktion ist kein Vorteil.
2

Mit der lodash-Bibliothek können Sie _.omitBy verwenden

var a = _.omitBy({
    b: conditionB ? 4 : undefined,
    c: conditionC ? 5 : undefined,
}, _.IsUndefined)

Dies ist praktisch, wenn Sie optionale Anforderungen haben

var a = _.omitBy({
    b: req.body.optionalA,  //if undefined, will be removed
    c: req.body.optionalB,
}, _.IsUndefined)
htafoya
quelle
0

Ich denke, Ihr erster Ansatz, Mitglieder bedingt hinzuzufügen, ist vollkommen in Ordnung. Ich stimme nicht wirklich mit nicht Mitglied haben will , bder amit einem Wert von undefined. Es ist einfach genug, mit dem Operator eine undefinedPrüfung unter Verwendung einer forSchleife hinzuzufügen in. Auf jeden Fall können Sie leicht eine Funktion schreiben, um undefinedMitglieder herauszufiltern .

var filterUndefined = function(obj) {
  var ret = {};
  for (var key in obj) {
    var value = obj[key];
    if (obj.hasOwnProperty(key) && value !== undefined) {
      ret[key] = value;
    }
  }
  return ret;
};

var a = filterUndefined({
  b: (conditionB? 5 : undefined),
  c: (conditionC? 5 : undefined),
  d: (conditionD? 5 : undefined),
  e: (conditionE? 5 : undefined),
  f: (conditionF? 5 : undefined),
  g: (conditionG? 5 : undefined),
});

Sie können den deleteOperator auch verwenden , um das Objekt an Ort und Stelle zu bearbeiten.

Yunchi
quelle
0

In ein Objekt einwickeln

So etwas ist ein bisschen sauberer

 const obj = {
   X: 'dataX',
   Y: 'dataY',
   //...
 }

 const list = {
   A: true && 'dataA',
   B: false && 'dataB',
   C: 'A' != 'B' && 'dataC',
   D: 2000 < 100 && 'dataD',
   // E: conditionE && 'dataE',
   // F: conditionF && 'dataF',
   //...
 }

 Object.keys(list).map(prop => list[prop] ? obj[prop] = list[prop] : null)

In ein Array einwickeln

Oder wenn Sie die Methode von Jamie Hill verwenden möchten und eine sehr lange Liste von Bedingungen haben, müssen Sie die ...Syntax mehrmals schreiben . Um es ein bisschen sauberer zu machen, können Sie sie einfach in ein Array einbinden und dann reduce()als einzelnes Objekt zurückgeben.

const obj = {
  X: 'dataX',
  Y: 'dataY',
  //...

...[
  true && { A: 'dataA'},
  false && { B: 'dataB'},
  'A' != 'B' && { C: 'dataC'},
  2000 < 100 && { D: 'dataD'},
  // conditionE && { E: 'dataE'},
  // conditionF && { F: 'dataF'},
  //...

 ].reduce(( v1, v2 ) => ({ ...v1, ...v2 }))
}

Oder mit map()Funktion

const obj = {
  X: 'dataX',
  Y: 'dataY',
  //...
}

const array = [
  true && { A: 'dataA'},
  false &&  { B: 'dataB'},
  'A' != 'B' && { C: 'dataC'},
  2000 < 100 && { D: 'dataD'},
  // conditionE && { E: 'dataE'},
  // conditionF && { F: 'dataF'},
  //...

 ].map(val => Object.assign(obj, val))
Dedy Abdillah
quelle
0

Dies ist die prägnanteste Lösung, die ich finden kann:

var a = {};
conditionB && a.b = 5;
conditionC && a.c = 5;
conditionD && a.d = 5;
// ...
Subarachnid
quelle
-1

Mit der lodash-Bibliothek können Sie _.merge verwenden

var a = _.merge({}, {
    b: conditionB ? 4 : undefined,
    c: conditionC ? 5 : undefined,
})
  1. Wenn BedingungB false& BedingungC ist true, danna = { c: 5 }
  2. Wenn sowohl Bedingung B als auch Bedingung C sind true, danna = { b: 4, c: 5 }
  3. Wenn sowohl Bedingung B als auch Bedingung C sind false, danna = {}
user3437231
quelle
Ich bekomme ein anderes Ergebnis. Ich benutze lodash@^4.0.0. undefinedwerden in meinem Fall aufgenommen.
JohnnyQ