简体   繁体   English

JavaScript,如何给一个object有条件的加成员?

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

I would like to create an object with a member added conditionally.我想创建一个 object 并有条件地添加一个成员。 The simple approach is:简单的方法是:

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

Now, I would like to write a more idiomatic code.现在,我想写一个更地道的代码。 I am trying:我在尝试:

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

But now, b is a member of a whose value is undefined .但是现在, ba的成员,其值为undefined This is not the desired result.这不是想要的结果。

Is there a handy solution?有方便的解决方案吗?

Update更新

I seek for a solution that could handle the general case with several members.我寻求一种解决方案,可以处理多个成员的一般情况。

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

I think @InspiredJW did it with ES5, and as @trincot pointed out, using es6 is a better approach.我认为@InspiredJW 是用 ES5 做到的,正如@trincot 所指出的,使用 es6 是一种更好的方法。 But we can add a bit more sugar, by using the spread operator, and logical AND short circuit evaluation:但是我们可以通过使用扩展运算符和逻辑与短路评估来添加更多的糖:

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

Live Demo:现场演示:

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

In pure Javascript, I cannot think of anything more idiomatic than your first code snippet.在纯 Javascript 中,我想不出比您的第一个代码片段更惯用的东西了。

If, however, using the jQuery library is not out of the question, then $.extend() should meet your requirements because, as the documentation says:但是,如果使用 jQuery 库不是不可能的,那么$.extend()应该满足您的要求,因为正如文档所述:

Undefined properties are not copied.未定义的属性不会被复制。

Therefore, you can write:因此,您可以编写:

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

And obtain the results you expect (if conditionB is false , then b will not exist in a ).并获得您期望的结果(如果conditionBfalse ,则b将不存在于a中)。

I suggest the following:我建议如下:

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

With EcmaScript2015 you can use Object.assign :使用 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);

Some remarks:一些备注:

  • Object.assign modifies the first argument in-place, but it also returns the updated object: so you can use this method in a bigger expression that further manipulates the object. Object.assign就地修改了第一个参数,但它也返回了更新后的对象:因此您可以在更大的表达式中使用此方法,以进一步操作该对象。
  • Instead of null you could pass undefined or {} , with the same result.您可以传递undefined{}而不是null ,但结果相同。 You could even provide 0 instead, because primitive values are wrapped, and Number has no own enumerable properties .您甚至可以提供0代替,因为原始值被包装,并且Number没有自己的可枚举属性

Even more concise更简洁

Taking the second point further, you could shorten it as follows (as @Jamie has pointed out), as falsy values have no own enumerable properties ( false , 0 , NaN , null , undefined , '' , except document.all ):进一步考虑第二点,您可以将其缩短如下(正如@Jamie 指出的那样),因为虚假值没有自己的可枚举属性( 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);

Conditionally Add a member to an Object有条件地向对象添加成员

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

// { student: 10 }

Perfomance test性能测试

Classic approach经典方法

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

VS VS

spread operator approach传播算子方法

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

Results :结果

The classic approach is much faster, so take in consideration that the syntax sugaring is slower.经典方法要快得多,因此请考虑到语法糖化较慢。

testClassicConditionFulfilled(); testClassicConditionFulfilled(); // ~ 234.9ms // ~ 234.9 毫秒
testClassicConditionNotFulfilled(); testClassicConditionNotFulfilled(); // ~493.1ms // ~493.1ms
testSpreadOperatorConditionFulfilled(); testSpreadOperatorConditionFulfilled(); // ~2649.4ms // ~2649.4ms
testSpreadOperatorConditionNotFulfilled(); testSpreadOperatorConditionNotFulfilled(); // ~2278.0ms // ~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) ab = value; } console.timeEnd('testClassicConditionFulfilled'); } function testClassicConditionNotFulfilled() { const value = undefined; console.time('testClassicConditionNotFulfilled'); for (let i = 0; i < 200000000; i++) { let a = {}; if (value) ab = value; } console.timeEnd('testClassicConditionNotFulfilled'); } testClassicConditionFulfilled(); // ~ 234.9ms testClassicConditionNotFulfilled(); // ~493.1ms testSpreadOperatorConditionFulfilled(); // ~2649.4ms testSpreadOperatorConditionNotFulfilled(); // ~2278.0ms

more simplified,更简化,

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

What about using Enhanced Object Properties and only set the property if it is truthy, eg:使用增强的对象属性怎么样,并且只在它是真实的情况下设置属性,例如:

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

So if the condition is not met it doesn't create the preferred property and thus you can discard it.因此,如果不满足条件,则不会创建首选属性,因此您可以丢弃它。 See: http://es6-features.org/#ComputedPropertyNames请参阅: http ://es6-features.org/#ComputedPropertyNames

UPDATE: It is even better to follow the approach of Axel Rauschmayer in his blog article about conditionally adding entries inside object literals and arrays ( http://2ality.com/2017/04/conditional-literal-entries.html ):更新:最好遵循 Axel Rauschmayer 在他的博客文章中关于有条件地在对象文字和数组中添加条目的方法( http://2ality.com/2017/04/conditional-literal-entries.html ):

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

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

Quite helped me a lot.对我帮助很大。

Better answer:更好的答案:

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

SIMPLE ES6 SOLUTION简单的 ES6 解决方案

Single condition with (&)带有 (&) 的单一条件

 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)

Dual condition with (? :)具有 (? :) 的双重条件

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

Explanation解释

Let's say you have storage object like this假设您有这样的storage object

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

and you would like to add a prop to this conditionally based on score并且您想根据score有条件地为此添加道具

const score = 90

You would now like to add prop c:30 to storage if score is greater than 100 .如果score大于100 ,您现在想将c:30添加到storage中。

If score is less than 100 , then you want to add d:40 to storage .如果分数小于100 ,那么您要将d:40添加到storage中。 You can do like this你可以这样做

const score = 110

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

The above code gives storage as上面的代码给出了storage作为

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

If score = 90如果score = 90

then you get storage as然后你得到storage作为

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

Codepen example 代码笔示例

This is probably the shortest solution with ES6这可能是 ES6 中最短的解决方案

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

我会这样做

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

You can add all your undefined values with no condition and then use JSON.stringify to remove them all :您可以无条件添加所有未定义的值,然后使用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
// }

If the goal is to have the object appear self-contained and be within one set of braces, you could try this:如果目标是让对象看起来是独立的并且在一组大括号内,你可以试试这个:

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

    if (conditionC)
        this.c = 5;

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

This has long been answered, but looking at other ideas I came up with some interesting derivative:这早就得到了回答,但是看着其他想法,我想出了一些有趣的衍生物:

Assign undefined values to the same property and delete it afterwards为同一属性分配未定义的值,然后将其删除

Create your object using an anonymous constructor and always assign undefined members to the same dummy member which you remove at the very end.使用匿名构造函数创建您的对象,并始终将未定义的成员分配给您在最后删除的同一虚拟成员。 This will give you a single line (not too complex I hope) per member + 1 additional line at the end.这将为每个成员提供一行(我希望不是太复杂)+ 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;
};

If you wish to do this server side (without jquery), you can use lodash 4.3.0:如果你希望做这个服务器端(没有 jquery),你可以使用 lodash 4.3.0:

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

And this works using lodash 3.10.1这可以使用 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.
}

I hope this is the much efficient way to add an entry based on the condition.我希望这是根据条件添加条目的有效方法。 For more info on how to conditionally add entries inside an object literals.有关如何有条件地在对象文字中添加条目的更多信息。

I made a small benchmark with one other option.我用另一种选择做了一个小基准测试 I like to remove "dead weight" from some objects.我喜欢从一些物体上去除“自重”。 Usually falsy values.通常是虚假值。

Here are the benny results:以下是本benny的结果:

clean干净的

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

clean({ value });

spread传播

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

if如果

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

results结果

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

Using lodash library, you can use _.omitBy使用 lodash 库,您可以使用_.omitBy

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

This results handy when you have requests that are optional当您有可选的请求时,这很方便

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

This is the most succinct solution I can come up with:这是我能想到的最简洁的解决方案:

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

i prefere, using code this it, you can run this code我更喜欢,使用它的代码,你可以运行这个代码

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);
const isAdult = true;

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

//>> { student: 18 }

I think your first approach to adding members conditionally is perfectly fine.我认为您第一种有条件地添加成员的方法非常好。 I don't really agree with not wanting to have a member b of a with a value of undefined .我真的不同意不希望 a 的成员b a值为undefined It's simple enough to add an undefined check with usage of a for loop with the in operator.使用带有in运算符的for循环添加undefined检查非常简单。 But anyways, you could easily write a function to filter out undefined members.但无论如何,您可以轻松编写一个函数来过滤掉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),
});

You could also use the delete operator to edit the object in place.您还可以使用delete运算符来编辑对象。

Below code snippet should work.下面的代码片段应该可以工作。

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

I hope this helps to solve your problem我希望这有助于解决您的问题

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

Using lodash library, you can use _.merge使用 lodash 库,您可以使用_.merge

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

Wrap into an object包装成一个对象

Something like this is a bit cleaner像这样的东西有点干净

 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)

Wrap into an array包装成一个数组

Or if you want to use Jamie Hill's method and have a very long list of conditions then you must write ... syntax multiple times.或者,如果您想使用 Jamie Hill 的方法并且有很长的条件列表,那么您必须多次编写...语法。 To make it a bit cleaner, you can just wrap them into an array, then use reduce() to return them as a single object.为了使其更简洁,您可以将它们包装到一个数组中,然后使用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 }))
}

Or using map() function或使用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))

Define a var by let and just assign new property通过let定义一个 var 并分配新属性

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

Now the msg become现在msg变成

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

In my opinion this is very simple and easy solution.在我看来,这是非常简单易行的解决方案。

For the sake of completeness you can use Object.defineProperty() if you want to add additional descriptors .为了完整起见,如果要添加其他描述符,可以使用Object.defineProperty() Note I purposely added enumerable: true otherwise the property wouldn't appear in the console.log() .请注意,我特意添加了enumerable: true ,否则该属性不会出现在console.log()中。 The advantage with this approach is that you can also use Object.defineProperties() if you want to add multiple new properties (However, in this way every property will be dependent on the same condition...)这种方法的优点是,如果要添加多个新属性,还可以使用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>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM