Як у 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
Так, є; перший біт коду.
Паоло Бергантіно

5
Не впевнений, що існує таке поняття, як ідіоматичний JavaScript ...
Michael Berkowski

Це насправді має значення? Якщо ви ніколи не визначилися a.b, пошук a.bвсе undefinedодно повернеться .
Теему

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. Подивіться на відповідь Джеймі Хіла.
Ендрю Расмуссен

13
@Andrew, ця відповідь вимагає ES6, який не існував у той час, коли я писав свою.
Фредерік Хаміді

чи нульовий працює так само? чи це має бути невизначеним?
Aous1000

Це насправді неправильна відповідь, оскільки він використовує jQuery, і ця потрійна умова не видалить властивість з об’єкта, це просто встановить властивість як невизначене. Дивіться відповідь @lagistos, як правильно це зробити,
Олександр Кім

Так перевірити відповідь Джеймі нижче: stackoverflow.com/a/40560953/10222449
MadMac

867

Я думаю, що @InspiredJW зробив це з ES5, і як зазначав @trincot, використання es6 є кращим підходом. Але ми можемо додати трохи більше цукру за допомогою оператора спред та логічної оцінки І короткого замикання:

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

2
Я не впевнений , що це правильно, Пропозиція держави Null/Undefined Are Ignored, це не говорить , falseігнорується. Транспілери можуть це дозволити на даний момент, але чи він відповідає? Наступне повинно бути, {...someCondition ? {b: 5} : null}але не настільки компактно.
Бенджамін Добелл

23
Я запитав, чи справедливо це для людей, які зробили пропозицію про поширення, і вони сказали, що це добре. github.com/tc39/proposed-object-rest-spread/isissue/45 , cc @BenjaminDobell
김민준

73
Оператор розповсюдження @AlanH - це як скорочення Object.assignта має нижчий пріоритет, ніж оператор && Він ігнорує значення без властивості (boolean, null, undefined, number), і додає всі властивості об'єкта після ...місця. пам'ятайте, що &&оператор повертає правильне значення, якщо це істинне, або неправдиве інше. тому, якщо someConditionце правда, {b : 5}буде передано ...оператору, що призведе до додавання властивості bдо aзначення 5. є someConditionпомилковим, falseбуде передано ...оператору. в результаті нічого не додається. це розумно. Я це люблю.
Фелікс Брунет

11
Чудова відповідь, але розміщення умови та розповсюдження отриманого об'єкта в дужках значно покращить читабельність цього прикладу. Не всі пам’ятають пріоритет оператора JS напам'ять.
Нейромедіатор

6
Єдине інше питання - ви не можете використовувати це для помилкових булів.
ggb667

92

З EcmaScript2015 ви можете використовувати Object.assign:

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

Деякі зауваження:

Ще більш лаконічним

Беручи другу точку далі, ви могли б скоротити його наступним чином (як @Jamie вказував), а falsy значення не мають власних перелічуваних властивостей ( false, 0, NaN, null, undefined, '', за винятком document.all):

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


1
Я віддаю перевагу початковому рішенню: простіше зрозуміти, що відбувається - я не був би впевнений, що відбувається з примітивним значенням (принаймні, не, не дивлячись на специфікацію).
Аксель Раушмайер

1
Я люблю стенографію, яка спрощує потрійну логіку. Це саме те, що мені було потрібно. Дякую!
другий

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

Демонстрація демонстрації:

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

console.log(obj);


7
Хоча цей фрагмент коду може вирішити питання, зокрема пояснення дійсно допомагає покращити якість вашої публікації. Пам’ятайте, що ви відповідаєте на запитання читачів у майбутньому, і ці люди можуть не знати причини вашої пропозиції щодо коду.
Ральф Стубнер

1
Що ця відповідь додає до відповіді Джеймі Хілла на 2 роки раніше ?
Дан Даскалеску

якщо cond не збігається, це повернеться невизначеним.
Mustkeem K

2
Це працює, і він має кращий синтаксис, ніж будь-який з інших відповідей ІМО.
Seph Reed

1
Коротке пояснення виглядає так: "..." оператор розповсюдження деконструює об'єкт буквально і додає його до "obj", наприклад, у цьому випадку ... (true) && {someprop: 42}, весь термін, який повинен бути деконструйований, це "(true) && {someprop: 42}", в цьому випадку булевий істинний, і термін просто дає {someprop: 42}, який потім деконструюється і додається в obj. якщо натомість булевий помилковий, то термін буде просто помилковим, і нічого не буде деконструйовано та додано в obj
Qiong Wu

50

Використання синтаксису спред з булевим (як запропоновано тут) не є синтаксисом. Спред можна використовувати лише з ітерабелізаторами .

Я пропоную наступне:

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

2
@HossamMourad, це не повинно, оскільки запропонований код вже використовувався у відповіді, опублікованій більше 2 років тому. І перше зауваження хибне. Це дійсний синтаксис:{...false}
trincot

1
@Itai, це неправда; примітивні значення загортаються при використанні з синтаксисом розповсюдження. {...false}дійсний синтаксис.
трінкот

@trincot, чи можете ви надати довідку?
Ітаї Ноам

3
Специфікація мови EcmaScript 2018, розділ 12.2.6 (.8) визначає, що CopyDataPropertiesвиконується за значенням ( falseу цьому випадку). Це стосується боксу примітиву (розділи 7.3.23, 7.1.13). Посилання, яке ви маєте до MDN, в дужках згадує про це "виключення".
трінкот

Як довідку, дивіться також цю публікацію, де я розкриваю цю тему.
трінкот

26

А як щодо використання вдосконалених властивостей об’єкта та встановити властивість лише у тому випадку, якщо воно є правдоподібним, наприклад:

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

Отже, якщо умова не виконується, вона не створює бажану властивість, і, таким чином, ви можете її відкинути. Дивіться: http://es6-features.org/#ComputedPropertyNames

ОНОВЛЕННЯ: Ще краще дотримуватися підходу Акселя Раушмаєра в його статті про блог про умовне додавання записів всередині об'єктних літералів та масивів ( 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}це{a:17, false: 42}
viebel

3
Я знайшов більш стислий спосіб: ...isConditionTrue() && { propertyName: 'propertyValue' }
Димитрій Рейфшнайдер

Кращий спосіб: ... (isConditionTrue ()? {Key: 'value'}: {})
Dimitri Reifschneider

Ця відповідь дає посилання на блог Axel Rauschmayer. Приклад "... insertIf (cond, 'a") "у статті - саме те, що я шукав. Спасибі
Джозеф Сімпсон


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

І це працює за допомогою лодашу 3.10.1

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

Немає необхідності в квартирі в ES6.
Дан Даскалеску

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

Я сподіваюся, що це набагато ефективніший спосіб додати запис на основі умови. Для отримання додаткової інформації про те, як умовно додати записи всередині об'єкта літерали.


3
[...condition?'':['item']]це додасть рядковий елемент у масив
Wayou

Чим ця відповідь краща за відповідь Джеймі Хілла на рік раніше ?
Дан Даскалеску

1
@DanDascalescu Відповідь Джеймі Хілла краща за мою відповідь, я не думав таким чином, і я був швидше хлопцем-тренажером.
Мадханкумар

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

Відредаговано однією версією кодового рядка


якщо умова хибна, а анонсований, що невірно.
bingjie2680

@ Bingjie2680 Це не те, що ясно , що це повинно бути , коли someCondition є хибним. Я тільки припустив . Це можна легко змінити за допомогою : /a = undefinedreturn { b: undefined };
jwchang

@ Bingjie2680 Тоді це повинно бути return {};в elseрамках
jwchang

1
Це насправді не те, що ви б робили ... це? Я маю на увазі, навіть якщо ви мали би зробити весь буквальний синтаксис умовним, як у вас є, чому б ви не використали умовний оператор? a = condition ? {b:5} : undefined;

3
З повагою, ви щойно перетворили три мертві прості лінії у 7-рядкову функцію без жодного посилення. Ні, використання анонімної функції не є користю.

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)

Загорніть у масив

Або якщо ви хочете скористатися методом Джеймі Хілла і маєте дуже довгий перелік умов, тоді ви повинні написати ...синтаксис кілька разів. Щоб зробити його трохи чистішим, ви можете просто загорнути їх у масив, а потім використовувати 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. Якщо умоваB є false& умоваC є true, тоa = { c: 5 }
  2. Якщо обидва умовиB і умоваC true, значить,a = { b: 4, c: 5 }
  3. Якщо обидва умовиB і умоваC false, значить,a = {}

Я отримую інший результат. Я використовую lodash@^4.0.0. undefinedвключаються до моєї справи.
JohnnyQ
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.