En Javascript, ¿cómo agregar condicionalmente un miembro a un objeto?


387

Me gustaría crear un objeto con un miembro agregado condicionalmente. El enfoque simple es:

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

Ahora, me gustaría escribir un código más idiomático. Estoy intentando:

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

Pero ahora, bes un miembro de acuyo valor es undefined. Este no es el resultado deseado.

¿Hay alguna solución útil?

Actualizar

Busco una solución que pueda manejar el caso general con varios miembros.

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
Sí hay; El primer bit de código.
Paolo Bergantino

66
No estoy seguro de que exista un JavaScript idiomático ...
Michael Berkowski

¿Realmente importa? Si nunca definió a.b, la recuperación a.bvolvería de undefinedtodos modos.
Teemu

66
@Teemu: podría importar cuándo inse utiliza el operador.

1
No hay forma de tener propiedades condicionales en objetos literales por ahora, pero me gustaría que lo agreguen en ES7, ¡esto podría ser muy útil, especialmente en la programación del lado del servidor!
Ali

Respuestas:


118

En Javascript puro, no puedo pensar en nada más idiomático que su primer fragmento de código.

Sin embargo, si el uso de la biblioteca jQuery no está descartado, entonces $ .extend () debería cumplir con sus requisitos porque, como dice la documentación:

Las propiedades no definidas no se copian.

Por lo tanto, puedes escribir:

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

Y obtenga los resultados que espera (si conditionBes así false, entonces bno existirá a).


21
Hay un método mucho mejor que no requiere jQuery. Echa un vistazo a la respuesta de Jamie Hill.
Andrew Rasmussen

13
@ Andrew, esa respuesta requiere ES6, que no existía cuando escribí la mía.
Frédéric Hamidi

funciona nulo de la misma manera? o tiene que ser indefinido?
Aous1000

En realidad, esta es una respuesta incorrecta, ya que utiliza jQuery y esta condición ternaria no eliminará una propiedad de un objeto, solo establecería una propiedad como indefinida. Consulte la respuesta de @lagistos para conocer la forma correcta de hacer esto
Alexander Kim,

mira

873

Creo que @InspiredJW lo hizo con ES5, y como señaló @trincot, usar es6 es un mejor enfoque. Pero podemos agregar un poco más de azúcar, utilizando el operador de propagación y la evaluación lógica Y de cortocircuito:

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

2
No estoy tan seguro de que esto sea correcto, dice la propuestaNull/Undefined Are Ignored , no dice que falsese ignore. Los transpiladores pueden permitir esto en este momento, pero ¿es compatible? Lo siguiente debería ser {...someCondition ? {b: 5} : null}pero no es tan compacto.
Benjamin Dobell

24
Pregunté si esto era válido para las personas que hicieron la propuesta de propagación y dijeron que está bien. github.com/tc39/proposal-object-rest-spread/issues/45 , cc @BenjaminDobell
김민준

73
El operador de propagación @AlanH es como una abreviatura de Object.assigny tiene menor prioridad que el operador &&. Ignora el valor sin propiedad (booleano, nulo, indefinido, número) y agrega todas las propiedades del objeto después del ...lugar. recuerde que el &&operador devuelve el valor correcto si es verdadero o falso de lo contrario. por lo que si someConditiones cierto, {b : 5}será pasado al ...operador, lo que resulta en la adición de la propiedad bque acon valor 5. es someConditionfalso, se falsepasará al ...operador. resultando en nada agregado. Es inteligente. Me encanta.
Félix Brunet

11
Gran respuesta, pero poner la condición y el objeto resultante que se extiende entre paréntesis mejorará en gran medida la legibilidad de este ejemplo. No todos recuerdan la precedencia del operador JS de memoria.
Neurotransmisor

66
El único otro problema es que no puede usar esto para falsos booleanos.
ggb667

92

Con EcmaScript2015 puede usar Object.assign:

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

Algunas observaciones:

Aún más conciso

Teniendo además el segundo punto, se podría acortar la siguiente manera (como @Jamie tiene cabo puntas), como valores Falsy no tienen propiedades enumerables propias ( false, 0, NaN, null, undefined, '', excepto document.all):

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


1
Prefiero la solución inicial: más fácil de entender qué está pasando, no estaría seguro de lo que sucede con un valor primitivo (al menos no sin mirar la especificación).
Axel Rauschmayer

1
Me encanta la taquigrafía que simplifica la lógica ternaria. Esto es exactamente lo que necesitaba. ¡Gracias!
theUtherSide

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

Demo en vivo:

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

console.log(obj);


77
Si bien este fragmento de código puede resolver la pregunta, incluir una explicación realmente ayuda a mejorar la calidad de su publicación. Recuerde que está respondiendo la pregunta para los lectores en el futuro, y que esas personas podrían no conocer los motivos de su sugerencia de código.
Ralf Stubner

1
¿Qué agrega esta respuesta a la respuesta de Jamie Hill de 2 años antes ?
Dan Dascalescu

si cond no coincide, esto devolverá indefinido.
Mustkeem K

2
Esto funciona, y tiene una sintaxis más agradable que cualquiera de las otras respuestas IMO.
Seph Reed

1
La explicación breve es la siguiente: el operador de propagación "..." deconstruye el objeto literal y lo agrega a "obj", por ejemplo, en este caso ... (verdadero) && {someprop: 42}, todo el término que se debe deconstruir es "(verdadero) && {someprop: 42}", en este caso el booleano es verdadero y el término solo produce {someprop: 42} que luego se deconstruye y se agrega a obj. si el booleano es falso en su lugar, entonces el término será falso, y nada será deconstruido y agregado al obj
Qiong Wu

50

El uso de sintaxis extendida con booleano (como se sugiere aquí) no es una sintaxis válida. Spread solo se puede usar con iterables .

Sugiero lo siguiente:

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

2
@HossamMourad, no debería, ya que el código sugerido ya se usó en una respuesta publicada hace más de 2 años. Y el primer comentario es falso. Esta es una sintaxis válida:{...false}
trincot

1
@Itai, esto no es cierto; los valores primitivos se envuelven cuando se usan con la sintaxis de propagación. {...false}es una sintaxis válida
Trincot

@trincot, ¿puede proporcionar una referencia?
Itai Noam

3
Especificación del lenguaje EcmaScript 2018, la sección 12.2.6 (.8) especifica que CopyDataPropertiesse realiza en el valor ( falseen este caso). Esto implica boxear una primitiva (secciones 7.3.23, 7.1.13). El enlace que tiene a MDN menciona esta "excepción" entre paréntesis.
Trincot

Como referencia, vea también esta publicación donde amplío sobre este mismo tema.
Trincot

26

¿Qué pasa con el uso de propiedades de objetos mejorados y solo establece la propiedad si es verdadera, por ejemplo:

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

Entonces, si no se cumple la condición, no crea la propiedad preferida y, por lo tanto, puede descartarla. Ver: http://es6-features.org/#ComputedPropertyNames

ACTUALIZACIÓN: es aún mejor seguir el enfoque de Axel Rauschmayer en su artículo de blog sobre agregar condicionalmente entradas dentro de literales y matrices de objetos ( http://2ality.com/2017/04/conditional-literal-entries.html ):

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

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

Me ayudó mucho.


1
Casi funcionará. El problema es que agregará una falseclave adicional . Por ejemplo, {[true && 'a']: 17, [false && 'b']: 42}es{a:17, false: 42}
viebel

3
Encontré una manera más concisa: ...isConditionTrue() && { propertyName: 'propertyValue' }
Dimitri Reifschneider

Mejor manera: ... (isConditionTrue ()? {Key: 'value'}: {})
Dimitri Reifschneider

El enlace del blog Axel Rauschmayer hace esta respuesta. El ejemplo "... insertIf (cond, 'a')" en el artículo es exactamente lo que estaba buscando. Gracias
Joseph Simpson

21

más simplificado

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

5

Si el objetivo es que el objeto parezca autónomo y esté dentro de un conjunto de llaves, puede intentar esto:

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

    if (conditionC)
        this.c = 5;

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

5

Si desea hacer este lado del servidor (sin jquery), puede usar lodash 4.3.0:

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

Y esto funciona usando lodash 3.10.1

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

No hay necesidad de lodash en ES6.
Dan Dascalescu

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

Espero que esta sea la forma más eficiente de agregar una entrada en función de la condición. Para obtener más información sobre cómo agregar entradas condicionalmente dentro de un objeto literal.


3
[...condition?'':['item']]esto agregará un elemento de cadena en la matriz
Wayou

¿Cómo es esta respuesta mejor que la respuesta de Jamie Hill del año anterior ?
Dan Dascalescu

1
@DanDascalescu La respuesta de Jamie Hill es mejor que mi respuesta, no pensé de esa manera y solía ser más un operador ternario.
Madhankumar

4

Esto ha sido respondido durante mucho tiempo, pero mirando otras ideas se me ocurrió una derivada interesante:

Asigne valores indefinidos a la misma propiedad y elimínelos después

Cree su objeto utilizando un constructor anónimo y siempre asigne miembros indefinidos al mismo miembro ficticio que elimine al final. Esto le dará una sola línea (no demasiado compleja, espero) por miembro + 1 línea adicional al final.

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

Puede agregar todos sus valores indefinidos sin condiciones y luego usar JSON.stringifypara eliminarlos todos:

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

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

// Contents of cleaned:

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

2

Yo haría esto

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

Editado con una versión de código de línea


si la condición es falsa, a está desnudo, lo cual no es correcto.
bingjie2680

@ bingjie2680 No es que claro lo que una debe ser cuando somecondition es falsa. Solo lo asumí a = undefined. Se puede cambiar fácilmente con return { b: undefined };: /
jwchang

@ bingjie2680 Entonces debería ser return {};en elseparte
jwchang

1
Eso no es realmente lo que harías ... ¿verdad? Quiero decir, incluso si tuvieras que condicionar la sintaxis literal completa como lo has hecho, ¿por qué no usarías el operador condicional? a = condition ? {b:5} : undefined;

3
Felicidades, acabas de convertir tres líneas simples en una función de 7 líneas sin ganancia. No, usar una función anónima no es un beneficio.

2

Usando la biblioteca lodash, puedes usar _.omitBy

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

Esto resulta útil cuando tiene solicitudes que son opcionales

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

0

Creo que su primer enfoque para agregar miembros condicionalmente está perfectamente bien. Realmente no estoy de acuerdo con no querer tener un miembro bde aun valor de undefined. Es bastante simple agregar un undefinedcheque con el uso de un forbucle con el inoperador. Pero de todos modos, podría escribir fácilmente una función para filtrar undefinedmiembros.

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

También puede usar el deleteoperador para editar el objeto en su lugar.


0

Envolver en un objeto

Algo como esto es un poco más limpio

 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)

Envolver en una matriz

O si desea utilizar el método de Jamie Hill y tener una lista muy larga de condiciones, debe escribir la ...sintaxis varias veces. Para hacerlo un poco más limpio, puede envolverlos en una matriz y luego usarlos reduce()para devolverlos como un solo objeto.

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()función

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

Esta es la solución más sucinta que se me ocurre:

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

0

Defina una var por lety simplemente asigne una nueva propiedad

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",
      },
    ];
}

Ahora el msgconvertido

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

En mi opinión, esta es una solución muy simple y fácil.


-1

Usando la biblioteca lodash, puedes usar _.merge

var a = _.merge({}, {
    b: conditionB ? 4 : undefined,
    c: conditionC ? 5 : undefined,
})
  1. Si la condición B es falsey la condición C estrue , entoncesa = { c: 5 }
  2. Si tanto la condición B como la condición C son true, entoncesa = { b: 4, c: 5 }
  3. Si tanto la condición B como la condición C son false, entoncesa = {}

Obtengo un resultado diferente. Estoy usando lodash@^4.0.0. undefinedestán siendo incluidos en mi caso.
JohnnyQ
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.