In Javascript, come aggiungere condizionalmente un membro a un oggetto?


387

Vorrei creare un oggetto con un membro aggiunto in modo condizionale. L'approccio semplice è:

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

Ora, vorrei scrivere un codice più idiomatico. Sto provando:

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

Ma ora, bè un membro del acui valore è undefined. Questo non è il risultato desiderato.

C'è una soluzione utile?

Aggiornare

Cerco una soluzione in grado di gestire il caso generale con diversi membri.

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),
 };

23
Si C'è; il primo bit di codice.
Paolo Bergantino,

6
Non sono sicuro che esista JavaScript idiomatico ...
Michael Berkowski,

Importa davvero? Se non lo hai mai definito a.b, il recupero a.btornerebbe undefinedcomunque.
Teemu,

6
@Teemu: potrebbe importare quando insi utilizza l' operatore.

1
Per ora non c'è modo di avere proprietà condizionali negli oggetti letterali, ma vorrei che lo aggiungessero in ES7, questo potrebbe essere molto utile soprattutto nella programmazione lato server!
Ali,

Risposte:


118

In puro Javascript, non riesco a pensare a qualcosa di più idiomatico del tuo primo frammento di codice.

Se, tuttavia, l'uso della libreria jQuery non è fuori discussione, $ .extend () dovrebbe soddisfare i tuoi requisiti perché, come dice la documentazione:

Le proprietà non definite non vengono copiate.

Pertanto, puoi scrivere:

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

E ottieni i risultati che ti aspetti (se lo conditionBè false, allora bnon esisterà a).


21
C'è un metodo molto migliore che non richiede jQuery. Dai un'occhiata alla risposta di Jamie Hill.
Andrew Rasmussen,

13
@Andrew, quella risposta richiede ES6, che non esisteva al momento in cui ho scritto il mio.
Frédéric Hamidi,

null funziona allo stesso modo? o deve essere indefinito?
Aous1000,

Questa è in realtà una risposta sbagliata, perché utilizza jQuery e questa condizione ternaria non rimuoverà una proprietà da un oggetto, questo imposterebbe semplicemente una proprietà come non definita. Vedi la risposta di @lagistos per il modo corretto di farlo,
Alexander Kim il

Yep controllare la risposta di Jamie qui di seguito: stackoverflow.com/a/40560953/10222449
MadMac

873

Penso che @InspiredJW l'abbia fatto con ES5 e, come sottolineato da @trincot, usare es6 è un approccio migliore. Ma possiamo aggiungere un po 'più di zucchero, usando l'operatore spread, e una valutazione logica E di corto circuito:

const a = {
   ...(someCondition && {b: 5})
}

2
Non sono così sicuro che sia corretto, afferma la propostaNull/Undefined Are Ignored , non dice di falseessere ignorato. I transpilers possono permetterlo attualmente, ma è conforme? Quanto segue dovrebbe essere {...someCondition ? {b: 5} : null}ma non è così compatto.
Benjamin Dobell,

24
Ho chiesto se questo era valido per le persone che hanno fatto una proposta diffusa e hanno detto che va bene. github.com/tc39/proposal-object-rest-spread/issues/45 , cc @BenjaminDobell
김민준

73
L'operatore di spread @AlanH è come una scorciatoia di Object.assigne ha una precedenza inferiore rispetto all'operatore &&. Ignora il valore senza proprietà (booleano, null, non definito, numero) e aggiunge tutte le proprietà dell'oggetto dopo il ...posto. ricordare che l' &&operatore restituisce il valore corretto se vero, o falso altrimenti. quindi se someConditionè vero, {b : 5}verrà passato al ...pilota, con conseguente aggiunta la proprietà bdi acon valore 5. è someConditionfalso, falseverrà passato ...all'operatore. risultante in nulla aggiunto. è intelligente. Lo adoro.
Félix Brunet,

11
Ottima risposta, ma mettere la condizione e l'oggetto risultante che si sta diffondendo tra parentesi migliorerà notevolmente la leggibilità di questo esempio. Non tutti ricordano a memoria la precedenza dell'operatore JS.
Neurotrasmettitore

6
L'unico altro problema è che non puoi usarlo per falsi booleani.
ggb667,

92

Con EcmaScript2015 è possibile utilizzare Object.assign:

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

Alcune osservazioni:

  • Object.assign modifica il primo argomento sul posto, ma restituisce anche l'oggetto aggiornato: quindi è possibile utilizzare questo metodo in un'espressione più grande che manipola ulteriormente l'oggetto.
  • Invece di nullte potresti passare undefinedo {}, con lo stesso risultato. Potresti anche fornire 0invece, poiché i valori primitivi sono racchiusi e Numbernon ha proprietà enumerabili .

Ancora più conciso

Prendendo ulteriormente il secondo punto, si potrebbe ridurre come segue (come @Jamie ha fuori a punta), come valori falsy non hanno proprie proprietà enumerabili ( false, 0, NaN, null, undefined, '', tranne document.all):

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


1
Preferisco la soluzione iniziale: più facile da capire cosa sta succedendo - non sarei sicuro di cosa succede con un valore primitivo (almeno non senza guardare le specifiche).
Axel Rauschmayer,

1
Adoro la stenografia che semplifica la logica ternaria. Questo è esattamente ciò di cui avevo bisogno. Grazie!
theUtherSide

80
const obj = {
   ...(condition) && {someprop: propvalue},
   ...otherprops
}

Dimostrazione dal vivo:

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

console.log(obj);


7
Mentre questo frammento di codice può risolvere la domanda, inclusa una spiegazione aiuta davvero a migliorare la qualità del tuo post. Ricorda che stai rispondendo alla domanda per i lettori in futuro e che queste persone potrebbero non conoscere i motivi del tuo suggerimento sul codice.
Ralf Stubner,

1
Cosa aggiunge questa risposta alla risposta di Jamie Hill di 2 anni prima ?
Dan Dascalescu,

se cond non corrisponde a questo restituirà indefinito.
Mustkeem K,

2
Funziona e ha una sintassi migliore di qualsiasi altra risposta IMO.
Seph Reed,

1
Una breve spiegazione è la seguente: "..." l'operatore spread decostruisce l'oggetto letteralmente e lo aggiunge a "obj", ad esempio in questo caso ... (vero) && {someprop: 42}, l'intero termine da decostruire è "(true) && {someprop: 42}", in questo caso il valore booleano è vero e il termine produce solo {someprop: 42} che viene quindi decostruito e aggiunto in obj. se invece il booleano è falso, allora il termine sarà solo falso e nulla verrà decostruito e aggiunto in obj
Qiong Wu,

50

L'uso della sintassi diffusa con booleano (come suggerito qui) non è sintassi valida. Lo spread può essere utilizzato solo con iterables .

Suggerisco quanto segue:

const a = {
   ...(someCondition? {b: 5}: {} )
}

2
@HossamMourad, non dovrebbe, poiché il codice suggerito è già stato utilizzato in una risposta pubblicata più di 2 anni fa. E la prima osservazione è falsa. Questa è sintassi valida:{...false}
trincot

1
@Itai, questo non è vero; i valori primitivi vengono racchiusi quando utilizzati con la sintassi di diffusione. {...false}è una sintassi valida.
trincot

@trincot, puoi fornire riferimento?
Itai Noam,

3
Specifiche del linguaggio EcmaScript 2018, sezione 12.2.6 (.8) specifica che CopyDataPropertiesviene eseguito sul valore ( falsein questo caso). Ciò comporta l'inscatolamento di una primitiva (sezioni 7.3.23, 7.1.13). Il collegamento che hai a MDN menziona questa "eccezione" tra parentesi.
trincot

Come riferimento, vedi anche questo post in cui espanderò su questo stesso argomento.
trincot

26

Che ne pensi dell'utilizzo di Enhanced Object Properties e imposta la proprietà solo se è vera, ad esempio:

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

Quindi se la condizione non è soddisfatta non crea la proprietà preferita e quindi puoi scartarla. Vedi: http://es6-features.org/#ComputedPropertyNames

AGGIORNAMENTO: È ancora meglio seguire l'approccio di Axel Rauschmayer nel suo articolo di blog sull'aggiunta condizionale di voci all'interno di letterali e array di oggetti ( http://2ality.com/2017/04/conditional-literal-entries.html ):

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

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

Mi ha aiutato molto.


1
Funzionerà quasi. Il problema è che aggiungerà una falsechiave aggiuntiva . Ad esempio, {[true && 'a']: 17, [false && 'b']: 42}è{a:17, false: 42}
viebel il

3
Ho trovato un modo più conciso: ...isConditionTrue() && { propertyName: 'propertyValue' }
Dimitri Reifschneider il

Modo migliore: ... (isConditionTrue ()? {Key: 'value'}: {})
Dimitri Reifschneider

Il link al blog di Axel Rauschmayer fornisce questa risposta. L'esempio "... insertIf (cond, 'a')" nell'articolo è esattamente quello che stavo cercando. Grazie
Joseph Simpson,

21

più semplificato,

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

5

Se l'obiettivo è che l'oggetto appaia autonomo e si trovi all'interno di un set di parentesi graffe, è possibile provare questo:

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

    if (conditionC)
        this.c = 5;

    if (conditionD)
        this.d = 5;
};

5

Se si desidera eseguire questo lato server (senza jquery), è possibile utilizzare lodash 4.3.0:

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

E questo funziona usando lodash 3.10.1

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

Non è necessario alloggiare in ES6.
Dan Dascalescu,

5
var a = {
    ...(condition ? {b: 1} : '') // if condition is true 'b' will be added.
}

Spero che questo sia il modo molto efficace per aggiungere una voce in base alla condizione. Per ulteriori informazioni su come aggiungere in modo condizionale voci all'interno di un oggetto letterali.


3
[...condition?'':['item']]questo aggiungerà l'elemento stringa nell'array
Wayou

In che modo questa risposta è migliore della risposta di Jamie Hill di un anno prima ?
Dan Dascalescu,

1
@DanDascalescu La risposta di Jamie Hill è migliore della mia risposta, non pensavo in quel modo e ero più un tipo da operatore ternario.
Madhankumar,

4

Questa è stata a lungo una risposta, ma guardando altre idee ho trovato alcuni derivati ​​interessanti:

Assegnare valori non definiti alla stessa proprietà ed eliminarlo in seguito

Crea il tuo oggetto usando un costruttore anonimo e assegna sempre membri indefiniti allo stesso membro fittizio che rimuovi alla fine. Questo ti darà una sola riga (spero non troppo complessa) per membro + 1 riga aggiuntiva alla fine.

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;
};

4

È possibile aggiungere tutti i valori non definiti senza alcuna condizione e quindi utilizzare JSON.stringifyper rimuoverli tutti:

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

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

// Contents of cleaned:

// cleaned = {
//   age: 22,
//   height: null
// }

2

Vorrei fare questo

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

Modificato con una versione del codice di linea


se la condizione è falsa, a è nudo, che non è corretto.
bingjie2680,

@ bingjie2680 Non è chiaro quello che una dovrebbe essere quando someCondition è falsa. Ho appena assunto a = undefined. Può essere facilmente modificato con return { b: undefined };: /
jwchang

@ bingjie2680 Quindi dovrebbe essere return {};in elseparte
jwchang,

1
Non è davvero quello che faresti ... vero? Voglio dire, anche se dovessi rendere l'intera sintassi letterale condizionata come hai, perché non dovresti usare l'operatore condizionale? a = condition ? {b:5} : undefined;

3
Complimenti, hai appena trasformato tre linee semplicissime in una funzione a 7 linee senza alcun guadagno. No, l'utilizzo di una funzione anonima non è un vantaggio.

2

Usando la libreria lodash, puoi usare _.omitBy

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

Ciò risulta utile quando si hanno richieste opzionali

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

0

Penso che il tuo primo approccio per aggiungere membri in modo condizionale sia perfettamente soddisfacente. Io in realtà non sono d'accordo con il non voler avere un membro bdi aun valore undefined. È abbastanza semplice aggiungere un undefinedcontrollo con l'uso di un forciclo con l' inoperatore. Ma comunque, potresti facilmente scrivere una funzione per filtrare i undefinedmembri.

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),
});

È inoltre possibile utilizzare l' deleteoperatore per modificare l'oggetto sul posto.


0

Avvolgere in un oggetto

Qualcosa del genere è un po 'più pulito

 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)

Avvolgere in un array

Oppure, se si desidera utilizzare il metodo di Jamie Hill e disporre di un elenco molto lungo di condizioni, è necessario scrivere la ...sintassi più volte. Per renderlo un po 'più pulito, puoi semplicemente avvolgerli in un array, quindi utilizzare reduce()per restituirli come un singolo oggetto.

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 }))
}

O usando la map()funzione

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))

0

Questa è la soluzione più concisa che posso trovare:

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

0

Definisci un var lete assegna semplicemente una nuova proprietà

let msg = {
    to: "hito@email.com",
    from: "hifrom@email.com",
    subject: "Contact form",    
};

if (file_uploaded_in_form) { // the condition goes here
    msg.attachments = [ // here 'attachments' is the new property added to msg Javascript object
      {
        content: "attachment",
        filename: "filename",
        type: "mime_type",
        disposition: "attachment",
      },
    ];
}

Ora msgdiventa

{
    to: "hito@email.com",
    from: "hifrom@email.com",
    subject: "Contact form",
    attachments: [
      {
        content: "attachment",
        filename: "filename",
        type: "mime_type",
        disposition: "attachment",
      },
    ]
}

Secondo me questa è una soluzione molto semplice e facile.


-1

Usando la libreria lodash, puoi usare _.merge

var a = _.merge({}, {
    b: conditionB ? 4 : undefined,
    c: conditionC ? 5 : undefined,
})
  1. Se conditionB è falsee conditionC è true, alloraa = { c: 5 }
  2. Se sono sia conditionB che conditionC true, alloraa = { b: 4, c: 5 }
  3. Se sono sia conditionB che conditionC false, alloraa = {}

Ho un risultato diverso. Sto usando lodash@^4.0.0. undefinedvengono inclusi nel mio caso.
JohnnyQ,
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.