Trong Javascript, làm thế nào để có điều kiện thêm một thành viên vào một đối tượng?


386

Tôi muốn tạo một đối tượng với một thành viên được thêm vào một cách có điều kiện. Cách tiếp cận đơn giản là:

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

Bây giờ, tôi muốn viết một mã thành ngữ hơn. Tôi đang cố gắng:

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

Nhưng bây giờ, blà một thành viên củaa có giá trị là undefined. Đây không phải là kết quả mong muốn.

Có một giải pháp tiện dụng?

Cập nhật

Tôi tìm kiếm một giải pháp có thể xử lý trường hợp chung với một số thành viên.

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
Có, có; bit đầu tiên của mã.
Paolo Bergantino

6
Không chắc chắn có một thứ như JavaScript thành ngữ ...
Michael Berkowski

Nó thực sự quan trọng? Nếu bạn không bao giờ xác định a.b, lấy lại a.bsẽ trở lại undefined.
Teemu

6
@Teemu: Nó có thể quan trọng khi insử dụng toán tử.

1
Hiện tại không có cách nào để có các thuộc tính có điều kiện trong các đối tượng theo nghĩa đen, nhưng tôi ước họ thêm nó vào ES7, điều này có thể rất tiện dụng đặc biệt là trong lập trình phía máy chủ!
Ali

Câu trả lời:


118

Trong Javascript thuần túy, tôi không thể nghĩ bất cứ điều gì thành ngữ hơn đoạn mã đầu tiên của bạn.

Tuy nhiên, nếu sử dụng thư viện jQuery không nằm ngoài câu hỏi đó, thì $ .extend () sẽ đáp ứng các yêu cầu của bạn bởi vì, như tài liệu nói:

Thuộc tính không xác định không được sao chép.

Do đó, bạn có thể viết:

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

Và có được kết quả mà bạn mong đợi (nếu conditionBfalse, thì bsẽ không tồn tại a).


21
Có một phương pháp tốt hơn nhiều mà không cần jQuery. Hãy xem câu trả lời của Jamie Hill.
Andrew Rasmussen

13
@Andrew, câu trả lời đó yêu cầu ES6, không tồn tại vào thời điểm tôi viết.
Frédéric Hamidi

null có hoạt động theo cùng một cách không? hoặc nó phải được xác định?
Aous1000

Đây thực sự là một câu trả lời sai, bởi vì nó sử dụng jQuery và điều kiện tạm thời này sẽ không xóa một thuộc tính khỏi một đối tượng, điều này sẽ đặt một thuộc tính là không xác định. Xem @lagistos trả lời cho cách chính xác để làm điều này,
Alexander Kim

Đúng, hãy xem câu trả lời của Jamie bên dưới: stackoverflow.com/a/40560953/10222449
MadMac

868

Tôi nghĩ rằng @InspiredJW đã làm điều đó với ES5 và như @trincot đã chỉ ra, sử dụng es6 là một cách tiếp cận tốt hơn. Nhưng chúng ta có thể thêm một chút đường, bằng cách sử dụng toán tử trải và đánh giá logic và ngắn mạch:

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

2
Tôi không chắc chắn điều này là chính xác, các đề xuất nêu rõ Null/Undefined Are Ignored, nó không nói falselà bị bỏ qua. Transpilers có thể cho phép điều này thông qua hiện tại, nhưng nó có tuân thủ không? Những điều sau đây nên {...someCondition ? {b: 5} : null}nhưng không phải là nhỏ gọn.
Benjamin Dobell

23
Tôi hỏi liệu điều này có hợp lệ với những người đưa ra đề xuất lan truyền hay không và họ nói điều này là ổn. github.com/tc39/proposal-object-rest-spread/issues/45 , cc @BenjaminDobell
김민준

73
Toán tử lây lan @AlanH giống như một tốc ký Object.assignvà có độ ưu tiên thấp hơn toán tử &&. Nó bỏ qua giá trị không có thuộc tính (boolean, null, không xác định, số) và thêm tất cả các thuộc tính của đối tượng sau ...vị trí. nhớ &&toán tử trả về đúng giá trị nếu đúng hoặc sai. Vì vậy, nếu someConditionlà đúng, {b : 5}sẽ được chuyển cho ...toán tử, dẫn đến việc thêm thuộc tính bvào agiá trị 5. là someConditionsai, falsesẽ được chuyển cho ...nhà điều hành. dẫn đến không có gì thêm vào. thật thông minh Tôi thích nó.
Félix Brunet

11
Câu trả lời tuyệt vời, nhưng đặt điều kiện và đối tượng kết quả được truyền vào dấu ngoặc đơn sẽ cải thiện đáng kể khả năng đọc của ví dụ này. Không phải ai cũng nhớ ưu tiên toán tử JS.
Chất dẫn truyền thần kinh

6
Vấn đề duy nhất khác là bạn không thể sử dụng điều này cho các booleans sai.
ggb667

92

Với EcmaScript2015 bạn có thể sử dụng Object.assign:

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

Một số nhận xét:

  • Object.assign sửa đổi đối số đầu tiên tại chỗ, nhưng nó cũng trả về đối tượng được cập nhật: vì vậy bạn có thể sử dụng phương thức này trong một biểu thức lớn hơn để thao tác thêm đối tượng.
  • Thay vì nullbạn có thể vượt qua undefinedhoặc {}, với kết quả tương tự. Bạn thậm chí có thể cung cấp 0thay thế, bởi vì các giá trị nguyên thủy được bao bọc và Numberkhông có thuộc tính vô số riêng .

Thậm chí ngắn gọn hơn

Lấy điểm thứ hai hơn nữa, bạn có thể rút ngắn nó như sau (như @Jamie có ra nhọn), như các giá trị falsy không có thuộc tính đếm riêng ( false, 0, NaN, null, undefined, '', ngoại trừ document.all):

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


1
Tôi thích giải pháp ban đầu: dễ dàng hơn để tìm hiểu những gì đang diễn ra - Tôi sẽ không chắc chắn điều gì xảy ra với một giá trị nguyên thủy (ít nhất là không phải không nhìn vào thông số kỹ thuật).
Axel Rauschmayer

1
Tôi yêu cách tốc ký đơn giản hóa logic ternary. Đây chính xác là những gì tôi cần. Cảm ơn bạn!
theStherSide 27/03/18

80
const obj = {
   ...(condition) && {someprop: propvalue},
   ...otherprops
}

Bản thử trực tiếp:

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

console.log(obj);


7
Mặc dù đoạn mã này có thể giải quyết câu hỏi, bao gồm một lời giải thích thực sự giúp cải thiện chất lượng bài đăng của bạn. Hãy nhớ rằng bạn đang trả lời câu hỏi cho độc giả trong tương lai và những người đó có thể không biết lý do cho đề xuất mã của bạn.
Ralf Stubner

1
Câu trả lời này thêm gì vào câu trả lời của Jamie Hill từ 2 năm trước ?
Dan Dascalescu

nếu cond không khớp hơn cái này sẽ trả về không xác định.
Mustkeem K

2
Điều này hoạt động và nó có cú pháp đẹp hơn bất kỳ câu trả lời nào khác IMO.
Seph Reed

1
Giải thích ngắn gọn như sau: "..." toán tử trải rộng giải mã đối tượng theo nghĩa đen và thêm nó vào "obj", ví dụ trong trường hợp này ... (đúng) && {someprop: 42}, toàn bộ thuật ngữ sẽ được giải mã là "(true) && {someprop: 42}", trong trường hợp này boolean là true và thuật ngữ chỉ mang lại {someprop: 42} sau đó được giải mã và thêm vào obj. nếu boolean là sai thay vào đó, thì thuật ngữ sẽ chỉ là sai và không có gì sẽ được giải mã và thêm vào obj
Qiong Wu

50

Sử dụng cú pháp lây lan với boolean (như được đề xuất ở đây) không phải là cú pháp hợp lệ. Lan truyền chỉ có thể được sử dụng với iterables .

Tôi đề nghị như sau:

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

2
@HossamMourad, không nên, vì mã đề xuất đã được sử dụng trong câu trả lời được đăng hơn 2 năm trước. Và nhận xét đầu tiên là sai. Đây là cú pháp hợp lệ:{...false}
trincot

1
@Itai, điều này không đúng; giá trị nguyên thủy được gói khi sử dụng với cú pháp lây lan. {...false}là cú pháp hợp lệ.
trincot

@trincot, bạn có thể vui lòng cung cấp tài liệu tham khảo?
Itai Noam

3
Đặc tả ngôn ngữ EcmaScript 2018, phần 12.2.6 (.8) chỉ định CopyDataPropertiesđược thực hiện trên giá trị ( falsetrong trường hợp này). Điều này liên quan đến quyền anh nguyên thủy (phần 7.3.23, 7.1.13). Liên kết bạn có MDN đề cập đến "ngoại lệ" này trong ngoặc đơn.
trincot

Để tham khảo, xem thêm bài đăng này , nơi tôi mở rộng về cùng chủ đề này.
trincot

26

Điều gì về việc sử dụng Thuộc tính đối tượng nâng cao và chỉ đặt thuộc tính nếu nó là sự thật, ví dụ:

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

Vì vậy, nếu điều kiện không được đáp ứng, nó không tạo ra thuộc tính ưa thích và do đó bạn có thể loại bỏ nó. Xem: http://es6-features.org/#ComputingPropertyNames

CẬP NHẬT: Thậm chí còn tốt hơn khi làm theo cách tiếp cận của Axel Rauschmayer trong bài viết trên blog của anh ấy về việc thêm điều kiện vào bên trong các đối tượng bằng chữ và mảng ( http://2ality.com/2017/04/conditable-literal-entries.html ):

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

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

Khá giúp tôi rất nhiều.


1
Nó sẽ gần như làm việc. Vấn đề là nó sẽ thêm một falsekhóa phụ . Chẳng hạn, {[true && 'a']: 17, [false && 'b']: 42}{a:17, false: 42}
viebel

3
Tôi tìm thấy một cách ngắn gọn hơn: ...isConditionTrue() && { propertyName: 'propertyValue' }
Dimitri Reifschneider

Cách tốt hơn: ... (isConditionTrue ()? {Key: 'value'}: {})
Dimitri Reifschneider

Liên kết blog Axel Rauschmayer đưa ra câu trả lời này. Ví dụ "... insert If (cond, 'a')" trong bài viết chính xác là những gì tôi đang tìm kiếm. Cảm ơn
Joseph Simpson

20

đơn giản hơn,

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

5

Nếu mục tiêu là để đối tượng xuất hiện khép kín và nằm trong một bộ niềng răng, bạn có thể thử điều này:

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

    if (conditionC)
        this.c = 5;

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

5

Nếu bạn muốn làm phía máy chủ này (không có jquery), bạn có thể sử dụng lodash 4.3.0:

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

Và điều này hoạt động bằng cách sử dụng lodash 3.10.1

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

Không cần lodash trong ES6.
Dan Dascalescu

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

Tôi hy vọng đây là cách hiệu quả hơn để thêm một mục dựa trên điều kiện. Để biết thêm thông tin về cách thêm điều kiện vào các mục bên trong một đối tượng bằng chữ.


3
[...condition?'':['item']]điều này sẽ thêm mục chuỗi vào mảng
Wayou

Làm thế nào là câu trả lời này tốt hơn câu trả lời của Jamie Hill từ một năm trước ?
Dan Dascalescu

1
@DanDascalescu Câu trả lời của Jamie Hill tốt hơn câu trả lời của tôi, tôi đã không nghĩ theo cách đó và tôi từng là một người điều hành chim nhạn.
Madhankumar

4

Điều này đã được trả lời từ lâu, nhưng nhìn vào những ý tưởng khác tôi đã nghĩ ra một số dẫn xuất thú vị:

Gán các giá trị không xác định cho cùng một thuộc tính và xóa nó sau đó

Tạo đối tượng của bạn bằng cách sử dụng một hàm tạo ẩn danh và luôn gán các thành viên không xác định cho cùng một thành viên giả mà bạn xóa ở cuối. Điều này sẽ cung cấp cho bạn một dòng duy nhất (không quá phức tạp tôi hy vọng) cho mỗi thành viên + 1 dòng bổ sung vào cuối.

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

Bạn có thể thêm tất cả các giá trị không xác định của mình mà không cần điều kiện và sau đó sử dụng JSON.stringifyđể xóa tất cả chúng:

const person = {
  name: undefined,
  age: 22,
  height: null
}

const cleaned = JSON.parse(JSON.stringify(person));

// Contents of cleaned:

// cleaned = {
//   age: 22,
//   height: null
// }

2

Tôi sẽ làm điều này

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

Được chỉnh sửa với phiên bản mã một dòng


nếu điều kiện là sai, a được xác định, điều này không đúng.
bingjie2680

@ bingjie2680 Nó không phải là rõ ràng những gì nó một nên khi someCondition là sai. Tôi chỉ giả định a = undefined. Nó có thể dễ dàng thay đổi với return { b: undefined };: /
jwchang

@ bingjie2680 Sau đó, nó nên return {};trong elsephần
jwchang

1
Đó không thực sự là những gì bạn sẽ làm ... phải không? Ý tôi là ngay cả khi bạn đã làm cho toàn bộ cú pháp theo nghĩa đen có điều kiện như bạn có, tại sao bạn không sử dụng toán tử điều kiện? a = condition ? {b:5} : undefined;

3
Xin chúc mừng, bạn chỉ cần biến ba dòng đơn giản thành hàm 7 dòng không có lợi. Không, sử dụng một chức năng ẩn danh không phải là một lợi ích.

2

Sử dụng thư viện lodash, bạn có thể sử dụng _.omitBy

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

Kết quả này có ích khi bạn có các yêu cầu không bắt buộc

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

0

Tôi nghĩ rằng cách tiếp cận đầu tiên của bạn để thêm thành viên có điều kiện là hoàn toàn tốt. Tôi không thực sự đồng ý với không muốn có một thành viên bcủa amột giá trị undefined. Nó đủ đơn giản để thêm một undefinedkiểm tra với việc sử dụng forvòng lặp với intoán tử. Nhưng dù sao, bạn có thể dễ dàng viết một chức năng để lọc ra undefinedcác thành viên.

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

Bạn cũng có thể sử dụng deletetoán tử để chỉnh sửa đối tượng tại chỗ.


0

Quấn vào một vật

Một cái gì đó như thế này là một chút sạch sẽ hơn

 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)

Gói thành một mảng

Hoặc nếu bạn muốn sử dụng phương pháp của Jamie Hill và có một danh sách các điều kiện rất dài thì bạn phải viết ...cú pháp nhiều lần. Để làm cho nó sạch hơn một chút, bạn có thể chỉ cần bọc chúng thành một mảng, sau đó sử dụng reduce()để trả về chúng dưới dạng một đối tượng.

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

Hoặc sử dụng map()chức năng

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

Đây là giải pháp ngắn gọn nhất mà tôi có thể đưa ra:

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

-1

Sử dụng thư viện lodash, bạn có thể sử dụng _.merge

var a = _.merge({}, {
    b: conditionB ? 4 : undefined,
    c: conditionC ? 5 : undefined,
})
  1. Nếu conditionB là false& conditionC thì true,a = { c: 5 }
  2. Nếu cả hai điều kiệnB & conditionC là true, thìa = { b: 4, c: 5 }
  3. Nếu cả hai điều kiệnB & conditionC là false, thìa = {}

Tôi nhận được một kết quả khác nhau. Tôi đang sử dụng lodash@^4.0.0. undefinedđang được bao gồm trong trường hợp của tôi.
JohnnyQ
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.