ใน 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
ใช่นั่นคือ; รหัสบิตแรก
เปาโล Bergantino

5
ไม่แน่ใจว่ามีสิ่งเช่น JavaScript ที่ใช้สำนวน ...
Michael Berkowski

มันเป็นเรื่องสำคัญจริงหรือ หากคุณไม่เคยกำหนดการa.bเรียกคืนa.bก็จะกลับมาundefinedอยู่ดี
Teemu

5
@Teemu: มันอาจเกิดขึ้นเมื่อมีinการใช้ตัวดำเนินการ

1
ตอนนี้ไม่มีทางที่จะมีคุณสมบัติแบบมีเงื่อนไขในวัตถุตัวอักษร แต่ฉันหวังว่าพวกเขาจะเพิ่มมันใน ES7 นี้อาจมีประโยชน์มากโดยเฉพาะในการเขียนโปรแกรมฝั่งเซิร์ฟเวอร์!
อาลี

คำตอบ:


118

ใน Javascript ล้วนๆฉันไม่สามารถคิดอะไรที่แปลกประหลาดไปกว่าโค้ดขนาดสั้นของคุณ

อย่างไรก็ตามหากการใช้ห้องสมุด jQuery ไม่เป็นไปตามที่ต้องการ$ .extend ()ควรตรงตามความต้องการของคุณเพราะเอกสารอธิบายว่า:

คุณสมบัติที่ไม่ได้กำหนดจะไม่ถูกคัดลอก

ดังนั้นคุณสามารถเขียน:

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

และรับผลลัพธ์ที่คุณคาดหวัง (ถ้าconditionBเป็นfalseเช่นนั้นbจะไม่มีอยู่a)


21
มีวิธีที่ดีกว่ามากที่ไม่ต้องการ jQuery ดูคำตอบของ Jamie Hill
Andrew Rasmussen

13
@ แอนดรูว์คำตอบนั้นต้องใช้ ES6 ซึ่งไม่มีอยู่ตอนที่ฉันเขียนของฉัน
Frédéric Hamidi

null ทำงานในลักษณะเดียวกันได้อย่างไร หรือมันจะต้องไม่ได้กำหนด?
Aous1000

นี่เป็นคำตอบที่ผิดจริงเพราะใช้ jQuery และเงื่อนไขที่สามนี้จะไม่ลบคุณสมบัติออกจากวัตถุนี่จะเป็นการตั้งค่าคุณสมบัติเป็นไม่ได้กำหนด ดูคำตอบ @lagistos สำหรับวิธีที่ถูกต้องในการทำเช่นนี้
Alexander Kim

ดูคำตอบของเจมี่ด้านล่าง: stackoverflow.com/a/40560953/10222449
MadMac

868

ฉันคิดว่า @InspiredJW ทำกับ ES5 และตามที่ @trincot ชี้ให้เห็นว่าการใช้ es6 เป็นวิธีที่ดีกว่า แต่เราสามารถเพิ่มน้ำตาลได้อีกเล็กน้อยโดยใช้ตัวดำเนินการสเปรดและการประเมินตรรกะและลัดวงจร:

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

2
ฉันไม่แน่ใจว่าสิ่งนี้ถูกต้องหรือไม่ข้อเสนอจะระบุNull/Undefined Are Ignoredว่าไม่falseสนใจ ทรานสฟิลเลอร์อาจยอมให้สิ่งนี้ผ่านได้ในปัจจุบัน แต่มันใช้ได้หรือไม่? ต่อไปนี้ควรจะเป็น{...someCondition ? {b: 5} : null}แต่ไม่กะทัดรัด
Benjamin Dobell

23
ฉันถามว่าสิ่งนี้ใช้ได้กับคนที่ทำข้อเสนอการแพร่กระจายหรือไม่และพวกเขาบอกว่าสิ่งนี้ดี github.com/tc39/proposal-object-rest-spread/issues/45 , cc @BenjaminDobell
김민준

73
@AlanH ผู้ประกอบการสเปรดเป็นเหมือนชวเลขObject.assignและมีความสำคัญน้อยกว่า && ผู้ประกอบการ มันไม่สนใจค่าโดยไม่มีคุณสมบัติ (บูลีน, โมฆะ, ไม่ได้กำหนด, หมายเลข) และเพิ่มคุณสมบัติทั้งหมดของวัตถุหลังจากที่...ในสถานที่ จำ&&ผู้ประกอบการคืนค่าที่ถูกต้องถ้าจริงหรือเท็จอย่างอื่น ดังนั้นหากsomeConditionเป็นความจริง{b : 5}จะถูกส่งผ่านไปยัง...ผู้ประกอบการที่มีผลในการเพิ่มสถานที่ให้บริการbจะมีค่าa 5เป็นsomeConditionเท็จfalseจะถูกส่งไปยัง...ผู้ประกอบการ ส่งผลให้ไม่มีอะไรเพิ่ม มันฉลาด ฉันรักมัน.
Félix Brunet

11
คำตอบที่ยอดเยี่ยม แต่การวางเงื่อนไขและวัตถุที่เป็นผลลัพธ์ถูกกระจายไปในวงเล็บจะช่วยเพิ่มความสามารถในการอ่านตัวอย่างนี้ได้อย่างมาก ไม่ใช่ทุกคนที่จำความสำคัญของผู้ให้บริการ JS ด้วยใจ
สารสื่อประสาท

6
ปัญหาอื่น ๆ เพียงอย่างเดียวคือคุณไม่สามารถใช้สิ่งนี้เป็นเท็จบูลีน
ggb667

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 ไม่มีคุณสมบัตินับตัวเอง ( false, 0, NaN, null, undefined, ''ยกเว้นdocument.all):

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


1
ฉันชอบวิธีแก้ปัญหาเบื้องต้น: ง่ายกว่าที่จะทราบว่าเกิดอะไรขึ้น - ฉันไม่แน่ใจว่าเกิดอะไรขึ้นกับค่าดั้งเดิม
Axel Rauschmayer

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

1
คำตอบนี้เพิ่มไปยังคำตอบของ Jamie Hill เมื่อ 2 ปีก่อน ?
Dan Dascalescu

ถ้า cond ไม่ตรงกันกว่านี้จะส่งคืนไม่ได้กำหนด
Mustkeem K

2
ใช้งานได้และมีไวยากรณ์ที่ดีกว่า IMO คำตอบอื่นใด
Seph Reed

1
คำอธิบายสั้น ๆ จะเป็นดังนี้: "... " ตัวดำเนินการกระจาย deconstructs วัตถุตามตัวอักษรและเพิ่มลงใน "obj" เช่นในกรณีนี้ ... (จริง) && {someprop: 42} คำทั้งหมดที่จะถูกแยกออกคือ "(จริง) && {someprop: 42}" ในกรณีนี้บูลีนเป็นจริงและคำที่ให้ผลคือ {someprop: 42} ซึ่งจะถูกแยกออกและเพิ่มเข้าไปใน obj ถ้าบูลีนเป็นเท็จแทนคำนั้นจะเป็นเท็จและไม่มีอะไรจะถูกแยกออกและเพิ่มเข้าไปใน obj
Qiong Wu

50

การใช้ไวยากรณ์การแพร่กระจายกับบูลีน (ตามที่แนะนำที่นี่) ไม่ใช่ไวยากรณ์ที่ถูกต้อง สเปรดสามารถใช้ได้กับ iterablesเท่านั้น

ฉันขอแนะนำดังต่อไปนี้:

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

2
@HossamMourad ไม่ควรเนื่องจากรหัสที่แนะนำถูกใช้ไปแล้วในคำตอบที่โพสต์เมื่อ 2 ปีที่แล้ว และคำพูดแรกเป็นเท็จ นี่เป็นรูปแบบที่ถูกต้อง:{...false}
trincot

1
@Itai นี้ไม่เป็นความจริง ค่าดั้งเดิมถูกห่อเมื่อใช้กับไวยากรณ์การแพร่กระจาย {...false}เป็นไวยากรณ์ที่ถูกต้อง
trincot

@trotot, คุณช่วยให้การอ้างอิงโปรดหรือไม่
Itai Noam

3
ข้อมูลจำเพาะภาษา EcmaScript 2018 ส่วน 12.2.6 (.8) ระบุว่าCopyDataPropertiesจะดำเนินการกับค่า ( falseในกรณีนี้) สิ่งนี้เกี่ยวข้องกับการต่อสู้แบบดึกดำบรรพ์ (ส่วน 7.3.23, 7.1.13) ลิงค์ที่คุณต้อง MDN กล่าวถึง "ข้อยกเว้น" นี้ในวงเล็บ
trincot

เป็นข้อมูลอ้างอิงดูโพสต์นี้ที่ฉันขยายในเรื่องเดียวกันนี้
trincot

26

สิ่งที่เกี่ยวกับการใช้คุณสมบัติวัตถุที่ได้รับการปรับปรุงและตั้งค่าคุณสมบัติเฉพาะถ้าเป็นความจริงเช่น:

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

ดังนั้นหากเงื่อนไขไม่ตรงตามเงื่อนไขจะไม่สร้างคุณสมบัติที่ต้องการและทำให้คุณสามารถยกเลิกได้ ดู: http://es6-features.org/#ComputedPropertyNames

UPDATE: เป็นการดียิ่งขึ้นที่จะปฏิบัติตามแนวทางของ 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}is{a:17, false: 42}
viebel

3
ฉันพบวิธีที่กระชับมากขึ้น: ...isConditionTrue() && { propertyName: 'propertyValue' }
Dimitri Reifschneider

วิธีที่ดีกว่า: ... (isConditionTrue ()? {key: 'value'}: {})
Dimitri Reifschneider

ลิงก์บล็อกของ Axel Rauschmayer ทำให้คำตอบนี้ ตัวอย่าง "... insert If (cond, 'a')" ในบทความเป็นสิ่งที่ฉันกำลังมองหา ขอบคุณ
Joseph Simpson


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

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

ฉันหวังว่านี่เป็นวิธีที่มีประสิทธิภาพมากในการเพิ่มรายการตามเงื่อนไข สำหรับข้อมูลเพิ่มเติมเกี่ยวกับวิธีการเพิ่มรายการที่มีเงื่อนไขภายในตัวอักษรวัตถุ


3
[...condition?'':['item']]สิ่งนี้จะเพิ่มรายการสตริงลงในอาร์เรย์
Wayou

คำตอบนี้ดีกว่าคำตอบของ Jamie Hill จากปีก่อนอย่างไร
Dan Dascalescu

1
@DanDascalescu คำตอบของ Jamie Hill ดีกว่าคำตอบของฉันฉันไม่ได้คิดแบบนั้นและฉันเคยเป็นคนที่ประกอบไปด้วยพนักงานประกอบไปด้วยคนมากกว่า
Madhankumar

4

สิ่งนี้ได้รับคำตอบมานานแล้ว แต่การดูความคิดอื่น ๆ ฉันได้มากับอนุพันธ์ที่น่าสนใจ:

กำหนดค่าที่ไม่ได้กำหนดให้กับคุณสมบัติเดียวกันและลบทิ้งในภายหลัง

สร้างวัตถุของคุณโดยใช้คอนสตรัคเตอร์นิรนามและกำหนดสมาชิกที่ไม่ได้กำหนดให้กับสมาชิกหุ่นคนเดียวที่คุณลบออกในตอนท้าย สิ่งนี้จะทำให้คุณมีบรรทัดเดียว (ไม่ซับซ้อนเกินหวัง) ต่อสมาชิก + อีก 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;
};

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 = undefinedreturn { b: undefined };
jwchang

@ bingjie2680 แล้วมันควรจะเป็นreturn {};ในelseส่วนหนึ่ง
jwchang

1
ที่ไม่ได้จริงๆสิ่งที่คุณจะทำ ... มันคืออะไร? ฉันหมายถึงแม้ว่าคุณจะต้องสร้างไวยากรณ์ตามตัวอักษรตามเงื่อนไขอย่างที่คุณมีทำไมคุณไม่ใช้ตัวดำเนินการตามเงื่อนไข? a = condition ? {b:5} : undefined;

3
ขอแสดงความยินดีคุณเพียงแค่เปลี่ยนสามบรรทัดแบบง่าย ๆ ให้เป็นฟังก์ชั่น 7-line โดยที่ไม่ได้กำไร ไม่การใช้ฟังก์ชั่นที่ไม่ระบุชื่อนั้นไม่มีประโยชน์

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

ฉันคิดว่าวิธีแรกของคุณในการเพิ่มสมาชิกแบบมีเงื่อนไขนั้นดีมาก ผมไม่เห็นด้วยกับไม่ต้องการที่จะมีสมาชิกbของที่มีค่าของa undefinedง่ายพอที่จะเพิ่มการundefinedตรวจสอบด้วยการใช้งานforลูปกับinโอเปอเรเตอร์ แต่อย่างไรก็ตามคุณสามารถเขียนฟังก์ชันเพื่อกรอง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โอเปอเรเตอร์เพื่อแก้ไขวัตถุให้เข้าที่


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. ถ้าทั้งเงื่อนไข B & conditionC คือ trueเช่นนั้นa = { b: 4, c: 5 }
  3. ถ้าทั้งเงื่อนไข B & conditionC คือ falseเช่นนั้นa = {}

ฉันได้รับผลลัพธ์ที่แตกต่าง lodash@^4.0.0ฉันใช้ undefinedกำลังรวมอยู่ในกรณีของฉัน
JohnnyQ
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.