在 JavaScript 中,如何有条件地向对象添加成员

In JavaScript, how to conditionally add a member to an object?

本文关键字:对象 添加 成员 有条件 JavaScript      更新时间:2024-03-30

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

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

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

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

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

有没有方便的解决方案?

更新

我寻求一种能够处理几个成员的一般情况的解决办法。

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

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

const a = {
   ...(someCondition && {b: 5})
}
const obj = {
   ...(condition) && {someprop: propvalue},
   ...otherprops
}

现场演示:

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

我建议如下:

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

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

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

不会复制未定义的属性。

因此,您可以编写:

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

并获得您期望的结果(如果conditionB false,则ba中将不存在(。

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

一些评论:

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

更简洁

更进一步,你可以将其缩短如下(正如@Jamie所指出的(,因为 falsy 值没有自己的可枚举属性(false0NaNnullundefined'',除了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);

有条件地向对象添加成员

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

性能测试

经典方法

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

点差运算符方法

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

结果

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

testClassicConditionFulfilled((;//~ 234.9ms
testClassicConditionNotFul((;~493.1毫秒
testSpreadOperatorConditionFul((;~2649.4毫秒
testSpreadOperatorConditionNotFul((;~2278.0毫秒

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

更简化,

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

简单的 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)

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

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

解释

假设你有这样的对象storage

const storage = {
  a : 10,
  b : 20,
}

并且您想根据score有条件地为此添加一个道具

const score = 90

您现在想将道具c:30添加到storage如果score大于 100 .

如果分数小于 100 ,则您希望将d:40添加到storage 中。你可以这样做

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

上面的代码给出storage

{
  a: 10,
  b: 20,
  c: 30
}

如果score = 90

然后你会得到storage

{
  a: 10,
  b: 20,
  d: 40
}

代码笔示例

使用增强的对象属性并仅在真实时才设置属性怎么样,例如:

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

对我帮助很大。

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

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

我用另一个选项做了一个小的基准测试。我喜欢从某些物体上去除"自重"。通常是虚假值。

以下是benny结果:

干净

const clean = o => {
    for (const prop in o) if (!o) delete o[prop];
}
clean({ value });

传播

let a = {
    ...(value && {b: value})
};

如果

let a = {};
if (value) {
    a.b = value;
}

结果

clean  :  84 918 483 ops/s, ±1.16%    | 51.58% slower    
spread :  20 188 291 ops/s, ±0.92%    | slowest, 88.49% slower    
if     : 175 368 197 ops/s, ±0.50%    | fastest

我会这样做

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

如果目标是使对象看起来自包含并在一组大括号内,您可以尝试以下操作:

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

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

这个问题早就得到了回答,但看看其他想法,我想出了一些有趣的推导:

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

使用匿名构造函数创建对象,并始终将未定义的成员分配给最后删除的同一虚拟成员。这将为每个成员提供一行(我希望不会太复杂(+ 最后 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;
};

下面的代码片段应该可以工作。

const a = {}
const conditionB = true;
const conditionC = true;
const conditionD = true;
const conditionE = true;
const b = {
  ...(conditionB && { b : 5}),
  ...(conditionC && { c : 5}),
  ...(conditionD && { d : 5}),
  ...(conditionE && { e : 5}),
 };
console.log(b);

如果你想做这个服务器端(没有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));
var a = {
    ...(condition ? {b: 1} : '') // if condition is true 'b' will be added.
}

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

const isAdult = true;
const obj = {
  ...(isAdult ? { age: 18 }: { age: 17}),
};
//>> { student: 18 }

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

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

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

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

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

为了扩展基于 ES6 的答案,我创建了这个实用程序 Typescript 函数,它使用法(在我看来(更具可读性,不像一个神奇的公式,意图非常明确,并且具有正确的类型:

/**
 * Creates a simple object with ot without the specified property and value, depending on the condition.
 * Usage: When creating an object literal that needs to include a property only in some cases.
 * Use it with the spread operator.
 * @example
 * const user = {
 *   username,
 *   // will include the property only if isInternalUser() returns true
 *   ...conditionalObjectProperty('password', isInternalUser(), () => getUserPassword())
 * }
 * @param propertyName
 * @param condition
 * @param valueCreator
 */
export function conditionalObjectProperty<P extends string, V> (propertyName: P, condition: boolean, valueCreator: () => V) {
  return condition
    ? { [propertyName]: valueCreator() }
    : {};
}
/**
 * Specialized conditional property creator that creates an object containing a specified property
 * only when its value is non-nullable.
 * Use in object literals with the spread operator.
 * @example
 * const middleName: string|undefined = getMiddleName();
 * const user = {
 *   userName,
 *   firstName,
 *   lastName,
 *   // will be included only if middleName is non-nullable
 *   ...optionalObjectProperty('middleName', middleName)
 * }
 * @param propertyName
 * @param value
 */
export function optionalObjectProperty<P extends string, V> (propertyName: P, value: V) {
  return conditionalObjectProperty(propertyName, value != null, () => value);
}
我认为

您有条件地添加成员的第一种方法非常好。我真的不同意不想让一个a的成员b,其值为 undefined.添加undefined检查与in运算符一起使用for循环非常简单。但无论如何,您可以轻松地编写一个函数来过滤掉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 运算符就地编辑对象。

我希望这有助于解决您的问题

<body>
  
    <h1>GeeksforGeeks</h1>
      
    <p id="geeks"></p>
  
      
    <!-- Script to check array include
        object or not -->
    <script>
        var obj = {"geeks1":10, "geeks2":12}
        var arr = ["geeks1", "geeks2", "geeks3", obj];
          
        if(arr.filter(value=> value==obj).length > 0)
            document.write("true");
        else
            document.write("false");
    </script>
</body>

使用 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 = {}

换行到对象中

像这样的东西更干净一些

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

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

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

为了完整起见,如果要添加其他描述符,可以使用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>