Удалите пустые свойства / ложные значения из объекта с помощью Underscore.js

84

У меня есть объект с несколькими свойствами. Я хочу удалить все свойства с ложными значениями.

Этого можно добиться с помощью compactмассивов, но как насчет объектов?


источник
Чтобы избежать копирования в репозитории, вы можете использовать Bit для импорта этого компонента (который имеет 3 прохождения теста и лицензию MIT). Вы также можете попробовать этот пакет NPM (который может оказаться излишним для небольшого компонента).
Йони

Ответы:

47

Вы можете создать свой собственный плагин подчеркивания (миксин):

_.mixin({
  compactObject: function(o) {
    _.each(o, function(v, k) {
      if(!v) {
        delete o[k];
      }
    });
    return o;
  }
});

А затем используйте его как собственный метод подчеркивания:

var o = _.compactObject({
  foo: 'bar',
  a: 0,
  b: false,
  c: '',
  d: null,
  e: undefined
});

Обновить

Как указал @AndreiNeculau , этот миксин влияет на исходный объект, в то время как исходный метод подчеркивания возвращает копию массива . Чтобы решить эту проблему и заставить нас вести себя как кузен , вот небольшое обновление:compact
compactObject

_.mixin({
  compactObject : function(o) {
     var clone = _.clone(o);
     _.each(clone, function(v, k) {
       if(!v) {
         delete clone[k];
       }
     });
     return clone;
  }
});
gion_13
источник
1
Поскольку в вопросе есть ссылка на подчеркивание, было бы хорошо упомянуть, что это не ведет себя как _.compact. Он удалит свойства, а не создаст мелкий клон только с истинными значениями. См stackoverflow.com/a/19750822/465684 ниже
Андрей Neculau
@AndreiNeculau Вы правы! Кажется, я раньше это упустил. Смотрите мой обновленный ответ.
gion_13 05
3
Зачем сначала копировать все свойства объекта, а затем перебирать их и удалять ложные? Это неэффективно. Более того, использование deleteобычно не рекомендуется, поскольку оно сразу же предоставляет свойства с тем же именем из цепочки прототипов, а также снижает производительность из-за «скрытых классов» (V8) - изменение структуры объекта заставляет движок выполнять дополнительную работу. Лучшее и самое короткое решение было бы _.pick(o, _.identity).
Радько Динев
171

Начиная с версии Underscore 1.7.0, вы можете использовать _.pick:

_.pick(sourceObj, _.identity)

Объяснение

Второй параметр _.pickможет быть функцией предиката для выбора значений. Выбираются значения, для которых предикат возвращает истинность , а значения, для которых предикат возвращает ложь , игнорируются.

pick _.pick (объект, * ключи)

Вернуть копию объекта , отфильтрованную, чтобы иметь значения только для ключей из белого списка (или массива допустимых ключей). В качестве альтернативы принимает предикат, указывающий, какие ключи выбрать.

_.identity- это вспомогательная функция, которая возвращает свой первый аргумент, что означает, что она также работает как функция-предикат, которая выбирает истинные значения и отклоняет ложные. Библиотека Underscore также поставляется с множеством других предикатов, например _.pick(sourceObj, _.isBoolean), сохранит только логические свойства.

Если вы часто используете эту технику, возможно, вы захотите сделать ее более выразительной:

var pickNonfalsy = _.partial(_.pick, _, _.identity); // Place this in a library module or something
pickNonfalsy(sourceObj);

Также предоставляется Underscore версии 1.6.0 _.pick, но она не принимает функцию предиката вместо белого списка.

Эмиль Лундберг
источник
2
Отдельное спасибо за упоминание _.identityфункции, очень удобно.
ivkremer
9
Это было очень удобно! Также можно использовать _.omit(sourceObj, _.isUndefined)для удаления только неопределенных значений (допускающих false, null, 0).
Бен Паттерсон
1
Также можно сделать pick(obj, Boolean)для устранения ложных значений тот же подход, который можно использовать при arr.filter(Boolean)очистке массива от ложных значений ...
Дэвид Чейз
3
В ES6 это превращается в_.pick(sourceObj, prop => prop)
Дениз Озгер
16
В lodash 4.4.0 _.pickработает с именами свойств, для этой функции, как указано в посте, используется_.pickBy
zooblin
46

Быстро и ясно: _.omitBy( source, i => !i );

Это сказано обратным ответу Эмиля. Таким образом imho читается яснее; это более понятно.

Чуть менее чистый, если у вас нет роскоши ES6: _.omitBy( source, function(i){return !i;});

Альтернативный: _.omitBy( source, _.isEmpty)

Использование _.isEmptyвместо _.identityправдивости также удобно удаляет из коллекции пустые массивы и объекты и, возможно, неудобно удаляет числа и даты . Таким образом, результат НЕ является точным ответом на вопрос OP, однако он может быть полезен при удалении пустых коллекций.

Швайдогг
источник
8
В Lodash 4.0 эта функциональность теперь недоступна omitBy. lodash.com/docs#omitBy
JackMorrissey 05
3
Я считаю, что это то же самое, что и: _.pick(source, i => i); что позволяет избежать отрицания
Джефф Лоури
2
@JeffLowery В Lodash это даже лучше, потому что предикатом по умолчанию является функция идентификации! _.pickBy(source)это все, что нужно.
Shibumi
Примечание: числа считаются пустыми. _.isEmpty(5) === true. Таким образом значения, которые являются числами, будут отброшены.
Сэр Натан Стассен
21

С преобразованием lodash ,

_.transform(obj, function(res, v, k) {
  if (v) res[k] = v;
});
дождь
источник
23
_.pick от lodash (obj, _.identity); короче ^ _ ^
evilive 09
Этот ответ или комментарий @ evilive под ним - это ответ.
Radko Dinev
2
более короткий вариант, основанный на вышеприведенном комментарии, будетvar compactObject = _.partialRight(_.pick, _.identity);
zaboco 08
yse, _.pickBy(object)это все, что вам нужно
wdetac
19
Object.keys(o).forEach(function(k) {
    if (!o[k]) {
        delete o[k];
    }
});
Флориан Маргейн
источник
1
И можно использовать подчеркивание для .keysи .forEach.
Феликс Клинг
Как тогда это будет выглядеть в Underscore? Пытаюсь собрать все воедино…
+1 это классный мужик. дайте пожалуйста ссылку на forEachметод JS
diEcho
@diEcho developer.mozilla.org/en-US/docs/JavaScript/Reference/…
Флориан Маргейн,
9

Вы можете создать неглубокий клон:

_(obj).reduce(function(a,v,k){ 
     if(v){ a[k]=v; } 
     return a; 
},{});
паутина
источник
5

для объекта используйте удаление.

for(var k in obj){

  if(obj.hasOwnProperty(k) && !obj[k]){
    delete obj[k];
  }
}
Anoop
источник
поскольку ему нужно решение с подчеркиванием, вы можете перебирать массив, используя один из методов подчеркивания
gion_13,
5

Внезапно мне потребовалось создать функцию для удаления рекурсивных фальсификаций. Надеюсь, это поможет. Я использую Lodash.

var removeFalsies = function (obj) {
    return _.transform(obj, function (o, v, k) {
        if (v && typeof v === 'object') {
            o[k] = _.removeFalsies(v);
        } else if (v) {
            o[k] = v;
        }
    });
};

_.mixin({ 'removeFalsies': removeFalsies });

Тогда вы можете использовать это:

var o = _.removeFalsies({
  foo: 'bar',
  a: 0,
  b: false,
  c: '',
  d: null,
  e: undefined,
  obj: {
    foo: 'bar',
    a: 0,
    b: false,
    c: '',
    d: null,
    e: undefined
  }
});

// {
//   foo: 'bar',
//   obj: {
//     foo: 'bar'
//   }
// }
Марко Годинес
источник
1

Чтобы добавить к ответу gion_13:

_.mixin({
  compactObject : function(o) {
     var newObject = {};
     _.each(o, function(v, k) {
       if(v !== null && v !== undefined) {
         newObject[k] = v
       }
     });
     return newObject;
  }
});

Он создает новый объект и добавляет ключи и значения вместо клонирования всего и удаления пар ключ-значение. Незначительная разница.

Но что еще более важно, он явно проверяет наличие null и undefined вместо falsey, что удаляет пары ключ-значение, которые имеют значение false.

Джефф Ли
источник
0

в lodash вы делаете это так:

_.pickBy(object, _.identity);
Хоссейн Резаи
источник
-1

Хотя _.compactзадокументирован для использования в массивах. Кажется, это работает и для объектов. Я только что запустил следующее в консолях chrome, opera и firefox:

var obj = {first: 1, second: null, third: 3, fourth: function(){return 5}}
undefined
_.compact(obj)

[1, 3, function()]

ОБНОВЛЕНИЕ: как показано в образце, вызов _.compactобъекта приведет к удалению ключей и возврату сжатого массива.

Цви
источник
1
Но он по-прежнему возвращает массив. Ключи потеряны.
Turadg
1
Вы правы. Могу ли я тогда удалить свой ответ? Или stackoverflow предпочитает что-то другое?
tzvi 05
2
Я не знаю предпочтений сообщества, но если вы согласны с тем, чтобы уйти, это может иметь значение, чтобы помешать кому-либо добавить аналогичный ответ.
Turadg 06