【问题标题】:In JavaScript, how to conditionally add a member to an object?在 JavaScript 中,如何有条件地向对象添加成员?
【发布时间】:2021-11-04 07:04:24
【问题描述】:

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

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

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

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

但是现在,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),
 };

【问题讨论】:

  • 不确定是否有像 JavaScript 这样的惯用语...
  • 这真的重要吗?如果您从未定义过a.b,则检索a.b 无论如何都会返回undefined
  • @Teemu:何时使用 in 运算符可能很重要。
  • @amnotiam 哎呀......这是真的,似乎我没有考虑所有方面......
  • 目前没有办法在文字对象中添加条件属性,但我希望他们在 ES7 中添加它,这可能非常方便,尤其是在服务器端编程中!

标签: javascript


【解决方案1】:

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

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

【讨论】:

  • 我不太确定这是否正确,proposal 声明 Null/Undefined Are Ignored,它并没有说 false 被忽略。转译器目前可能允许这样做,但它是否合规?以下应该是{...someCondition ? {b: 5} : null},但不是那么紧凑。
  • 我问这对提出传播建议的人是否有效,他们说这很好。 github.com/tc39/proposal-object-rest-spread/issues/45,抄送@BenjaminDobell
  • @AlanH 扩展运算符类似于Object.assign 的简写,其优先级低于 && 运算符。它忽略没有属性的值(boolean、null、undefined、number),并在... 之后添加对象的所有属性。请记住 && 运算符在 true 时返回正确的值,否则返回 false。因此,如果someCondition 为真,{b : 5} 将被传递给... 运算符,从而将属性b 添加到a,值为5。是someCondition 是假的,false 将被传递给... 运算符。导致没有添加任何内容。这很聪明。我喜欢它。
  • 很好的答案,但是将条件和结果对象放在括号中将大大提高此示例的可读性。不是每个人都牢记 JS 运算符的优先级。
  • 唯一的另一个问题是您不能将它用于虚假布尔值。
【解决方案2】:
const obj = {
   ...(condition) && {someprop: propvalue},
   ...otherprops
}

现场演示:

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

console.log(obj);

【讨论】:

  • 虽然这段代码 sn-p 可以解决问题,但including an explanation 确实有助于提高帖子的质量。请记住,您是在为将来的读者回答问题,而这些人可能不知道您提出代码建议的原因。
  • 这个答案对Jamie Hill's answer from 2 years earlier有什么作用?
  • 这行得通,而且它的语法比 IMO 的任何其他答案都好。
  • 简短解释如下:“...”扩展运算符解构对象文字并将其添加到“obj”,例如在这种情况下 ...(true) && {someprop: 42},要解构的整个术语是“(true) && {someprop: 42}”,在这种情况下,布尔值是 true 并且术语只是产生 { someprop:42} 然后解构并添加到 obj.如果布尔值是假的,那么这个词就是假的,什么都不会被解构并添加到 obj 中
  • @basickarl,不,它当然不应该是选择的答案,因为这里前两个 cmets 中提到的原因。
【解决方案3】:

在纯 Javascript 中,我想不出比你的第一个代码 sn-p 更惯用的了。

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

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

因此,你可以这样写:

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

并获得你期望的结果(如果conditionBfalse,那么b将不存在于a中)。

【讨论】:

  • 有一个更好的方法,它不需要 jQuery。看看 Jamie Hill 的回答。
  • @Andrew,这个答案需要 ES6,在我写我的时候还不存在。
  • null 的工作方式是否相同?还是必须是未定义的?
  • 这实际上是一个错误的答案,因为它使用 jQuery 并且这个三元条件不会从对象中删除属性,这只会将属性设置为未定义。请参阅@lagistos 答案以了解正确的方法,
  • @viebel,您是否愿意接受另一个答案而不是这个答案?
【解决方案4】:

使用 EcmaScript2015 你可以使用Object.assign:

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

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

一些备注:

更简洁

进一步考虑第二点,您可以将其缩短如下(正如@Jamie 指出的那样),因为虚假值没有自己的可枚举属性(false0NaNnull、@ 987654337@,''document.all除外):

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

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

【讨论】:

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

我建议如下:

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

【讨论】:

  • 这个答案太棒了!通过有条件地添加授权字段以在此处获取 POST 参数的示例做了一个要点:gist.github.com/mattlockyer/3dac7c9618ac98d16b046e32c364899d
  • 我喜欢这个!谢谢!
  • 这比流行的答案更可靠。如果condition 是一个对象,它会将其值注入到对象中,这可能会导致错误。
  • 简单有效。谢谢!
【解决方案6】:

有条件地向对象添加成员

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

// { student: 10 }

【讨论】:

    【解决方案7】:

    更简单,

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

    【讨论】:

    • 更简化?比您早 4 年给出的答案?我没看到你简化了什么...
    • 添加了一条评论...所以这意味着它是原始帖子,对吧?
    【解决方案8】:

    性能测试

    经典方法

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

    VS

    传播算子方法

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

    结果

    经典方法要快得多,因此请考虑到语法糖化较慢。

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

    function testSpreadOperatorConditionFulfilled() {
      const value = 5;
    
      console.time('testSpreadOperatorConditionFulfilled');
      for (let i = 0; i < 200000000; i++) {
        let a = {
          ...(value && {b: value})
        };
      }
      console.timeEnd('testSpreadOperatorConditionFulfilled');
    }
    
    function testSpreadOperatorConditionNotFulfilled() {
      const value = undefined;
    
      console.time('testSpreadOperatorConditionNotFulfilled');
      for (let i = 0; i < 200000000; i++) {
        let a = {
          ...(value && {b: value})
        };
      }
      console.timeEnd('testSpreadOperatorConditionNotFulfilled');
    }
    
    function testClassicConditionFulfilled() {
      const value = 5;
    
      console.time('testClassicConditionFulfilled');
      for (let i = 0; i < 200000000; i++) {
        let a = {};
        if (value)
            a.b = value;
      }
      console.timeEnd('testClassicConditionFulfilled');
    }
    
    function testClassicConditionNotFulfilled() {
      const value = undefined;
    
      console.time('testClassicConditionNotFulfilled');
      for (let i = 0; i < 200000000; i++) {
        let a = {};
        if (value)
            a.b = value;
      }
      console.timeEnd('testClassicConditionNotFulfilled');
    }
    
    testClassicConditionFulfilled(); // ~ 234.9ms
    testClassicConditionNotFulfilled(); // ~493.1ms
    testSpreadOperatorConditionFulfilled(); // ~2649.4ms
    testSpreadOperatorConditionNotFulfilled(); // ~2278.0ms

    【讨论】:

      【解决方案9】:

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

      [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'} : {})
      };
      

      对我帮助很大。

      【讨论】:

      • 它几乎可以工作。问题是它会添加一个额外的false 键。例如,{[true &amp;&amp; 'a']: 17, [false &amp;&amp; 'b']: 42}{a:17, false: 42}
      • 我找到了更简洁的方式:...isConditionTrue() &amp;&amp; { propertyName: 'propertyValue' }
      • 更好的方法:...(isConditionTrue() ? {key: 'value'} : {})
      • Axel Rauschmayer 博客链接给出了这个答案。文章中的“...insertIf(cond, 'a')”示例正是我所寻找的。谢谢
      【解决方案10】:

      这可能是 ES6 中最短的解决方案

      console.log({
         ...true && {foo: 'bar'}
      })
      // Output: {foo:'bar'}
      
      console.log({
         ...false && {foo: 'bar'}
      })
      // Output: {}
      

      【讨论】:

        【解决方案11】:

        我会这样做

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

        【讨论】:

        • @amnotiam 我实际上会使用 coffeescript 并且只是做a = if someCondition then b:5 else {}
        • 好多了,谢谢。现在唯一的问题是如果有强制参数(var o = cond ? {a: 1} : {a: 1, b: 2})会重复。
        • @InspiredJW:查看问题中的更新。我想处理一般情况。
        【解决方案12】:

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

        【讨论】:

          【解决方案13】:

          如果目标是让对象看起来是独立的并且在一组大括号内,你可以试试这个:

          var a = new function () {
              if (conditionB)
                  this.b = 5;
          
              if (conditionC)
                  this.c = 5;
          
              if (conditionD)
                  this.d = 5;
          };
          

          【讨论】:

            【解决方案14】:

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

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

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

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

            【讨论】:

              【解决方案15】:

              如果你希望做这个服务器端(没有 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。
              【解决方案16】:
              var a = {
                  ...(condition ? {b: 1} : '') // if condition is true 'b' will be added.
              }
              

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

              【讨论】:

              • [...condition?'':['item']] 这会将字符串项添加到数组中
              • 这个答案比Jamie Hill's answer from a year earlier好多少?
              • @DanDascalescu Jamie Hill 的回答比我的回答要好,我没有这样想,而且我以前更像是一个三元运算符。
              【解决方案17】:

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

              【讨论】:

                【解决方案18】:

                这是我能想到的最简洁的解决方案:

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

                【讨论】:

                  【解决方案19】:

                  我更喜欢,使用这个代码,你可以运行这个代码

                  const three = {
                    three: 3
                  }
                  
                  // you can active this code, if you use object `three is null`
                  //const three = {}
                  
                  const number = {
                    one: 1,
                    two: 2,
                    ...(!!three && three),
                    four: 4
                  }
                  
                  console.log(number);
                  

                  【讨论】:

                    【解决方案20】:

                    我认为您第一种有条件地添加成员的方法非常好。我真的不同意不想让a 的成员b 的值为undefined。使用for 循环和in 运算符添加undefined 检查非常简单。但无论如何,您可以轻松编写一个函数来过滤掉undefined 成员。

                    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 操作符来编辑对象。

                    【讨论】:

                      【解决方案21】:

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

                      var a = _.merge({}, {
                          b: conditionB ? 4 : undefined,
                          c: conditionC ? 5 : undefined,
                      })
                      
                      1. 如果条件B 是false 而条件C 是true,那么a = { c: 5 }
                      2. 如果条件B和条件C都是true,那么a = { b: 4, c: 5 }
                      3. 如果条件B和条件C都是false,那么a = {}

                      【讨论】:

                      • 我得到了不同的结果。我正在使用lodash@^4.0.0undefined 包含在我的案例中。
                      • @JohnnyQ 截至Lodash version 4.17.21_.merge({}, { x: undefined, y: 1 }); 返回{ "y": 1 }
                      【解决方案22】:

                      包装成一个对象

                      这样的东西比较干净

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

                      【讨论】:

                        【解决方案23】:

                        通过let 定义一个变量并分配新属性

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

                        现在msg 变成了

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

                        在我看来,这是非常简单易行的解决方案。

                        【讨论】:

                        • 您可以为对象添加新属性,即使它们是const
                        【解决方案24】:

                        为了完整起见,如果您想添加额外的descriptors,可以使用Object.defineProperty()。注意我特意添加了enumerable: true,否则该属性不会出现在console.log() 中。这种方法的优点是,如果您想添加多个新属性,也可以使用Object.defineProperties()(但是,这样每个属性都将依赖于相同的条件......)

                        const select = document.getElementById("condition");
                        const output = document.getElementById("output");
                        let a = {};
                        let b = {};
                        
                        select.onchange = (e) => {
                          const condition = e.target.value === "true";
                          condition
                            ? Object.defineProperty(a, "b", {
                                value: 5,
                                enumerable: true,
                              })
                            : (a = {});
                        
                          condition
                            ? Object.defineProperties(b, {
                                c: {
                                  value: 5,
                                  enumerable: true,
                                },
                                d: {
                                  value: 6,
                                  enumerable: true,
                                },
                                e: {
                                  value: 7,
                                  enumerable: true,
                                },
                              })
                            : (b = {});
                        
                          outputSingle.innerText = JSON.stringify(a);
                          outputMultiple.innerText = JSON.stringify(b);
                        };
                        Condition:
                        <select id="condition">
                          <option value="false">false</option>
                          <option value="true">true</option>
                        </select>
                        <br/>
                        <br/>
                        Single Property: <pre id="outputSingle">{}</pre><br/>
                        Multiple Properties: <pre id="outputMultiple">{}</pre>

                        【讨论】:

                          猜你喜欢
                          • 2015-07-04
                          • 2021-06-16
                          • 1970-01-01
                          • 1970-01-01
                          相关资源
                          最近更新 更多