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

vie*_*bel 294 javascript

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

var a = {};
if (someCondition)
    a.b = 5;
Run Code Online (Sandbox Code Playgroud)

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

a = {
    b: (someCondition? 5 : undefined)
};
Run Code Online (Sandbox Code Playgroud)

但现在,ba其价值的成员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),
 };
Run Code Online (Sandbox Code Playgroud)

小智 674

我认为@InspiredJW是用ES5做的,正如@trincot指出的那样,使用es6是一种更好的方法.但是我们可以通过使用扩展运算符和逻辑AND短路评估来添加更多的糖:

const a = {
   ...(someCondition && {b: 5})
}
Run Code Online (Sandbox Code Playgroud)

  • @AlanH spread运算符就像`Object.assign`的简写,并且优先级低于&&运算符.它忽略没有属性的值(boolean,null,undefined,number),并在`...`之后添加对象的所有属性.记住`&&`运算符如果为true则返回正确的值,否则返回false.因此,如果`someCondition`为真,`{b:5}`将被传递给`...`运算符,导致将属性`b`添加到值为'5`的`a`.是`someCondition`是假的,`false`将被传递给`...`运算符.没有添加任何内容.它很聪明.我喜欢它. (56认同)
  • 我问这对传播提案的人是否有效,他们说这很好.https://github.com/tc39/proposal-object-rest-spread/issues/45,cc @BenjaminDobell (16认同)
  • 很好的答案,但将条件和结果对象扩展到括号​​中将大大提高此示例的可读性.不是每个人都记得JS运营商的优先级. (7认同)
  • 我不太确定这是正确的,[提案](https://github.com/sebmarkbage/ecmascript-rest-spread/blob/master/Spread.md) 指出`Null/Undefined Are Ignored`,它确实不说 `false` 被忽略。转译器目前可能允许这样做,但它是否合规?以下应该是 `{...someCondition ? {b: 5} : null}` 但没有那么紧凑。 (6认同)
  • 唯一的其他问题是您不能将其用于错误的布尔值。 (4认同)
  • @ggb667 是的,你可以,双重否定`...(!!someFalsyCondition && {b: 5})` (3认同)
  • 小心,这也不适用于数组。在数组上传播 null 时传播运算符会抛出。https://github.com/tc39/ecma262/issues/687 (2认同)

Fré*_*idi 99

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

但是,如果使用jQuery库并不是不可能的,那么$ .extend()应该满足您的要求,因为文档说:

未复制未定义的属性.

因此,你可以写:

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

并获得您期望的结果(如果conditionBfalse,那么b将不存在a).

  • 有一个更好的方法,不需要jQuery.看看Jamie Hill的回答. (17认同)
  • @Andrew,答案需要ES6,这在我写作时并不存在. (11认同)
  • @viebel,您是否有机会接受另一个答案而不是这个答案? (3认同)
  • 这实际上是一个错误的答案,因为它使用 jQuery 并且这个三元条件不会从对象中删除属性,这只会将属性设置为未定义。请参阅@lagistos 答案以了解执行此操作的正确方法, (2认同)

tri*_*cot 82

使用EcmaScript2015,您可以使用Object.assign:

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

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

console.log(a);
Run Code Online (Sandbox Code Playgroud)

一些评论:

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

更简洁

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

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

var a, conditionB, conditionC, conditionD;
conditionC = "this is truthy";
conditionD = NaN; // falsy
a = {};
Object.assign(a, conditionB && { b: 1 },
                 conditionC && { c: 2 },
                 conditionD && { d: 3 });
console.log(a);
Run Code Online (Sandbox Code Playgroud)

  • 我更喜欢最初的解决方案:更容易弄清楚发生了什么 - 我不确定原始值会发生什么(至少在不查看规范的情况下不会)。 (3认同)
  • 我喜欢简化三元逻辑的速记。这正是我所需要的。谢谢! (2认同)

San*_*ath 59

简单的 ES6 解决方案

带 (&) 的单一条件 -if condition

const didIPassExam = true

const study = {
  monday : 'writing',
  tuesday : 'reading',
  
  /* check conditionally and if true, then add wednesday to study */

  ...(didIPassExam && {wednesday : 'sleep happily'})
}


console.log(study)
Run Code Online (Sandbox Code Playgroud)

双重条件 (?:) -if-else condition

const score = 110
//const score = 10

const storage = {
  a:10,
  b:20,
  ...(score > 100  ? {c: 30} : {d:40}) 
}

console.log(storage)
Run Code Online (Sandbox Code Playgroud)

解释

假设你有storage这样的对象

const storage = {
  a : 10,
  b : 20,
}
Run Code Online (Sandbox Code Playgroud)

并且您想根据条件添加一个道具score

const score = 90
Run Code Online (Sandbox Code Playgroud)

您现在想要添加 propc:30storageifscore大于100

如果分数小于100,那么您要添加 d:40storage。你可以这样做

const score = 110

const storage = {
  a:10,
  b:20,
  ...(score > 100  ? {c: 30} : {d:40}) 
}
Run Code Online (Sandbox Code Playgroud)

上面的代码给出storage如下

{
  a: 10,
  b: 20,
  c: 30
}
Run Code Online (Sandbox Code Playgroud)

如果score = 90

然后你storage得到

{
  a: 10,
  b: 20,
  d: 40
}
Run Code Online (Sandbox Code Playgroud)

Codepen 示例

  • 这是关于此语法(扩展语法)的 MDN 文档的[链接](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax#spread_in_object_literals) (2认同)

小智 39

更简化,

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

  • 更简化?比你四年前给出的答案更好吗?我没看出你简化了什么... (9认同)

Ita*_*oam 37

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

我建议以下内容:

const a = {
   ...(someCondition? {b: 5}: {} )
}
Run Code Online (Sandbox Code Playgroud)

  • 这个应该是公认的答案。 (4认同)
  • 这个答案太棒了!提出了一个要点,其中有条件地添加授权字段以获取 POST 参数:https://gist.github.com/mattlockyer/3dac7c9618ac98d16b046e32c364899d (2认同)

小智 35

性能测试

经典方法

const a = {};
if (someCondition)
    a.b = 5;
Run Code Online (Sandbox Code Playgroud)

VS

传播算子方法

const a2 = {
   ...(someCondition && {b: 5})
}
Run Code Online (Sandbox Code Playgroud)

结果

经典方法要快得多,因此请注意语法加糖速度较慢。

testClassicConditionFulfilled(); // ~ 234.9ms
testClassicConditionNotFulfilled(); // ~493.1ms
testSpreadOperatorConditionFulfilled(); // ~2649.4ms
testSpreadOperatorConditionNotFulfilled(); // ~2278.0ms

const a = {};
if (someCondition)
    a.b = 5;
Run Code Online (Sandbox Code Playgroud)

  • 实际上我不认为它与小 json 对象产生差异 (2认同)

Lag*_*tos 32

const obj = {
   ...(condition) && {someprop: propvalue},
   ...otherprops
}
Run Code Online (Sandbox Code Playgroud)

  • 尽管此代码段可以解决问题,但[包括说明](http://meta.stackexchange.com/questions/114762/explaining-entirely-code-based-answers)确实有助于提高您的帖子质量。请记住,您将来会为读者回答这个问题,而这些人可能不知道您提出代码建议的原因。 (7认同)
  • 这个答案对 [Jamie Hill 2 年前的回答](/sf/ask/819298721/ 40560953#40560953)? (4认同)
  • 简短的解释如下:“...”扩展运算符解构对象文字并将其添加到“obj”,例如在本例中...(true) && {someprop: 42},要解构的整个术语是“(true) && {someprop: 42}”,在这种情况下,布尔值为 true,并且该术语仅产生 {someprop:42},然后将其解构并添加到 obj 中。如果布尔值是 false,那么该术语将只是 false,并且不会解构任何内容并将其添加到 obj 中 (3认同)
  • 这是有效的,并且它的语法比任何其他答案都更好。 (2认同)
  • @basickarl,不,它当然不应该是选定的答案,因为这里的前两条评论中提到的原因。 (2认同)

Dim*_*der 24

如何使用增强对象属性并仅在属性真实时设置属性,例如:

[isConditionTrue() && 'propertyName']: 'propertyValue'
Run Code Online (Sandbox Code Playgroud)

因此,如果不满足条件,它不会创建首选属性,因此您可以丢弃它.请参阅: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'} : {})
};
Run Code Online (Sandbox Code Playgroud)

对我帮助很大.

  • 我找到了一个更简洁的方法:`... isConditionTrue()&& {propertyName:'propertyValue'}` (3认同)

Nor*_*Lin 16

更好的答案:

const a = {
   ...(someCondition ? {b: 5} : {})
}
Run Code Online (Sandbox Code Playgroud)

  • 这个答案并不比 2016 年的答案好。 (4认同)

San*_*ath 15

有条件地向对象添加成员

const trueCondition = true;
const falseCondition = false;
const obj = {
  ...(trueCondition && { student: 10 }),
  ...(falseCondition && { teacher: 2 }),
};

// { student: 10 }
Run Code Online (Sandbox Code Playgroud)


ice*_*tbr 13

我用另一种选择做了一个小基准。我喜欢消除某些物体的“自重”。通常是虚假值。

\n

结果如下benny

\n

干净的

\n
const clean = o => {\n    for (const prop in o) if (!o) delete o[prop];\n}\n\nclean({ value });\n
Run Code Online (Sandbox Code Playgroud)\n

传播

\n
let a = {\n    ...(value && {b: value})\n};\n
Run Code Online (Sandbox Code Playgroud)\n

如果

\n
let a = {};\nif (value) {\n    a.b = value;\n}\n
Run Code Online (Sandbox Code Playgroud)\n

结果

\n
clean  :  84 918 483 ops/s, \xc2\xb11.16%    | 51.58% slower    \nspread :  20 188 291 ops/s, \xc2\xb10.92%    | slowest, 88.49% slower    \nif     : 175 368 197 ops/s, \xc2\xb10.50%    | fastest\n
Run Code Online (Sandbox Code Playgroud)\n


mat*_*eos 9

This is probably the shortest solution with ES6

console.log({
   ...true && {foo: 'bar'}
})
// Output: {foo:'bar'}
Run Code Online (Sandbox Code Playgroud)
console.log({
   ...false && {foo: 'bar'}
})
// Output: {}
Run Code Online (Sandbox Code Playgroud)


Ins*_*dJW 6

我会这样做

var a = someCondition ? { b: 5 } : {};
Run Code Online (Sandbox Code Playgroud)


Mil*_*lad 6

您可以无条件添加所有未定义的值,然后使用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
// }
Run Code Online (Sandbox Code Playgroud)


Cas*_*Chu 5

如果目标是使对象看起来是自包含的并且在一组大括号内,您可以尝试这样做:

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

    if (conditionC)
        this.c = 5;

    if (conditionD)
        this.d = 5;
};
Run Code Online (Sandbox Code Playgroud)


Rob*_*nik 5

早就有人回答了这个问题,但是查看其他想法我想出了一些有趣的派生:

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

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

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;
};
Run Code Online (Sandbox Code Playgroud)


Mic*_*ckl 5

如果你想做这个服务器端(没有jquery),你可以使用lodash 4.3.0:

a = _.pickBy({ b: (someCondition? 5 : undefined) }, _.negate(_.isUndefined));
Run Code Online (Sandbox Code Playgroud)

这使用lodash 3.10.1

a = _.pick({ b: (someCondition? 5 : undefined) }, _.negate(_.isUndefined));
Run Code Online (Sandbox Code Playgroud)