在Javascript中,如何有条件地将成员添加到对象?


385

我想创建一个有条件添加成员的对象。简单的方法是:

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

现在,我想编写一个更惯用的代码。我在尝试:

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

但是,现在b是,a其值为的成员undefined。这不是期望的结果。

有方便的解决方案吗?

更新资料

我寻求一个可以解决几个成员的一般情况的解决方案。

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
就在这里; 代码的第一位。
Paolo Bergantino 2012年

5
不确定是否存在惯用JavaScript之类的东西
Michael Berkowski 2012年

真的重要吗?如果您从未定义a.b,则无论如何a.b将返回检索undefined
Teemu '07年

4
@Teemu:何时使用in运算符可能很重要。

1
目前尚无在文字对象中具有条件属性的方法,但我希望它们在ES7中添加它,这可能非常方便,尤其是在服务器端编程中!
阿里

Answers:


118

在纯Javascript中,我想不出比您的第一个代码片段更惯用的东西。

但是,如果不是不可能使用jQuery库,则$ .extend()应该符合您的要求,因为如文档所述:

未定义的属性不会被复制。

因此,您可以编写:

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

并获得您期望的结果(如果conditionBfalse,则在b中将不存在a)。


21
有一种更好的方法,不需要jQuery。看看杰米·希尔的答案。
Andrew Rasmussen

13
@Andrew,这个答案需要ES6,在我写我的时候还不存在。
弗雷德里克·哈米迪

null是否以相同的方式工作?还是必须不确定?
Aous1000

这实际上是一个错误的答案,因为它使用jQuery,并且这种三元条件不会从对象中删除属性,这只会将属性设置为undefined。有关正确的方法,请参见@lagistos答案,
亚历山大·金

是的,请查看以下Jamie的答案:stackoverflow.com/a/40560953/10222449
MadMac

867

我认为@InspiredJW是使用ES5完成的,正如@trincot指出的那样,使用es6是更好的方法。但是,通过使用散布运算符和逻辑与短路评估,我们可以添加更多的糖:

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

2
我不太确定这是正确的,该提案指出Null/Undefined Are Ignored,这并不是说false被忽略。编译器目前可以允许这样做,但是符合要求吗?以下内容应该是{...someCondition ? {b: 5} : null}但不那么紧凑。
本杰明·多贝尔

23
我问这对提出传播建议的人是否有效,他们说这很好。github.com/tc39/proposal-object-rest-spread/issues/45,抄送@BenjaminDobell
김민준

73
@AlanH扩展运算符就像&的简写,Object.assign并且其优先级比&&运算符低。它忽略不带属性的值(布尔,空,未定义,数字),并...在适当位置添加对象的所有属性。请记住,&&如果运算符为true,则返回正确的值,否则为false。因此,如果someCondition为true,{b : 5}则将其传递给...运算符,从而将属性添加bawith value中5。是someConditionfalse,false将传递给...运算符。结果什么也没添加。这很聪明。我喜欢它。
费利克斯·布吕

11
很好的答案,但是将条件和结果对象放在括号中将大大提高此示例的可读性。并非所有人都牢记JS运算符的优先级。
Neurotransmitter

6
唯一的其他问题是您不能将其用于错误的布尔值。
ggb667 '18

92

借助EcmaScript2015,您可以使用Object.assign

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

一些说明:

  • Object.assign 就地修改第一个参数,但它也会返回更新的对象:因此,您可以在更大的表达式中使用此方法,以进一步操作该对象。
  • 代替null您可以通过undefined{},结果相同。您甚至可以提供0,因为原始值是包装的,并且Number没有自己的可枚举属性

更简洁

进一步考虑第二点,你可以如下(@Jamie指出),作为falsy值没有自己的枚举的属性(缩短false0NaNnullundefined'',除外document.all):

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


1
我更喜欢最初的解决方案:更容易弄清发生了什么-我不确定原始值会发生什么(至少在不查看规格的情况下)。
Axel Rauschmayer's

1
我喜欢简化三元逻辑的速记。这正是我所需要的。谢谢!
theUtherSide

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

现场演示:

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

console.log(obj);


7
尽管此代码段可以解决问题,但提供说明确实有助于提高您的帖子质量。请记住,您将来会为读者回答这个问题,而这些人可能不知道您提出代码建议的原因。
Ralf Stubner '18


如果cond不匹配,则返回undefined。
Mustkeem K

2
这行得通,并且语法比IMO的其他答案更好。
Seph Reed

1
简短的解释是这样的:“ ...”传播运算符解构对象文字,并将其添加到“ obj”,例如,在这种情况下...(true)&& {someprop:42},整个要解构的项是“(true)&& {someprop:42}”,在这种情况下,布尔值是true,该术语仅产生{someprop:42},然后将其解构并添加到obj中。如果布尔值为false,则该术语将为false,并且不会分解任何内容并将其添加到obj
Qiong Wu

50

将扩展语法与布尔值一起使用(如此处建议的那样)是无效的语法。价差只能与可迭代对象一起使用。

我建议以下内容:

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

2
@HossamMourad,不应该这样,因为建议的代码已在两年多前发布的答案中使用。第一句话是错误的。这是有效的语法:{...false}
trincot

1
@Itai,这是不正确的;与传播语法一起使用时,原始值将被包装。{...false}是有效的语法。
亭子

@trincot,请您提供参考?
伊泰诺姆

3
EcmaScript 2018语言规范的12.2.6(.8)节指定CopyDataProperties对值执行(false在这种情况下)。这涉及将原语装箱(第7.3.23节,7.1.13节)。您到MDN的链接在括号中提到了此“例外”。
Trincot

作为参考,另请参阅本文,我将在同一主题上进行扩展。
燕窝

26

关于使用增强对象属性,并且仅在属实的情况下才设置属性,例如:

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

因此,如果不满足条件,则不会创建首选属性,因此可以将其丢弃。看到: http //es6-features.org/#ComputedPropertyNames

更新: 最好遵循Axel Rauschmayer在其博客文章中关于有条件地在对象文字和数组中添加条目的方法(http://2ality.com/2017/04/conditional-literal-entries.html):

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

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

相当帮助了我。


1
它将几乎可以工作。问题在于它将添加一个额外的false密钥。例如, {[true && 'a']: 17, [false && 'b']: 42}{a:17, false: 42}
viebel '17

3
我找到了一种更简洁的方法: ...isConditionTrue() && { propertyName: 'propertyValue' }
Dimitri Reifschneider

更好的方法:...(isConditionTrue()?{key:'value'}:{})
Dimitri Reifschneider

Axel Rauschmayer博客链接给出了此答案。本文中的示例“ ... insertIf(cond,'a')”正是我所要的。谢谢
约瑟夫·辛普森


5

如果目标是使对象显示为独立对象并放在一组括号内,则可以尝试以下操作:

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

    if (conditionC)
        this.c = 5;

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

5

如果您希望在服务器端执行此操作(不使用jquery),则可以使用lodash 4.3.0:

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

这可以使用lodash 3.10.1

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

在ES6中无需lodash。
Dan Dascalescu

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

我希望这是基于条件添加条目的高效方法。有关如何在对象文字中有条件地添加条目的更多信息。


3
[...condition?'':['item']]这会将字符串项添加到数组中
Wayou


1
@DanDascalescu杰米·希尔(Jamie Hill)的回答比我的回答要好。
Madhankumar

4

长期以来,人们已经回答了这个问题,但是在研究其他想法时,我想到了一些有趣的派生词:

将未定义的值分配给同一属性,然后将其删除

使用匿名构造函数创建对象,并始终将未定义的成员分配给最后要删除的同一虚拟成员。这将为您每个成员提供一行(我希望不太复杂),最后再增加一行。

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

您可以无条件添加所有未定义的值,然后使用JSON.stringify来删除它们:

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

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

// Contents of cleaned:

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

2

我会这样做

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

用一个行代码版本编辑


如果条件为假,则a为nudefined,这是不正确的。
bingjie2680

@ bingjie2680这并不是说清楚它是什么一个应该是当someCondition是假的。我只是假设a = undefined。可以使用以下命令轻松更改它return { b: undefined };:/
jwchang 2012年

@ bingjie2680那么它应该是return {};else部分
jwchang

1
那不是你真正想做的...是吗?我的意思是,即使您要按条件将整个文字语法设置为条件语法,为什么不使用条件运算符呢?a = condition ? {b:5} : undefined;

3
恭喜,您只是将3条简单的死线变成了7行函数而没有收益。不,使用匿名函数不是好处。

2

使用lodash库,您可以使用_.omitBy

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

当您有可选请求时,此结果很方便

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

0

我认为您有条件添加成员的第一种方法是完全可以的。我真的不与不希望有一个成员同意ba具有的价值undefined。添加与操作符循环undefined使用的检查非常简单。但是无论如何,您可以轻松编写一个函数来过滤成员。forinundefined

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

您还可以使用delete运算符在适当位置编辑对象。


0

包装成一个物体

这样的东西比较干净

 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)

包装成阵列

或者,如果您想使用Jamie Hill的方法并且条件列表很长,则必须...多次编写语法。为了使它更整洁,您可以将它们包装到一个数组中,然后reduce()用作单个对象来返回它们。

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

或使用map()功能

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

这是我能提出的最简洁的解决方案:

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

-1

使用lodash库,可以使用_.merge

var a = _.merge({}, {
    b: conditionB ? 4 : undefined,
    c: conditionC ? 5 : undefined,
})
  1. 如果conditionB是false&conditionC是true,则a = { c: 5 }
  2. 如果conditionB和conditionC都为 true,则a = { b: 4, c: 5 }
  3. 如果conditionB和conditionC都为false,则a = {}

我得到不同的结果。我正在使用lodash@^4.0.0undefined被包括在我的案子中。
JohnnyQ '18年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.