Фильтровать свойства объекта по ключу в ES6



допустим у меня есть объект:



{
item1: { key: 'sdfd', value:'sdfd' },
item2: { key: 'sdfd', value:'sdfd' },
item3: { key: 'sdfd', value:'sdfd' }
}


Я хочу создать другой объект путем фильтрации объекта выше, так что у меня есть что-то вроде.



 {
item1: { key: 'sdfd', value:'sdfd' },
item3: { key: 'sdfd', value:'sdfd' }
}


Я ищу чистый способ сделать это с помощью Es6, поэтому операторы распространения доступны для меня.
Спасибо!

1148   14  

14 ответов:

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

const raw = {
  item1: { key: 'sdfd', value:'sdfd' },
  item2: { key: 'sdfd', value:'sdfd' },
  item3: { key: 'sdfd', value:'sdfd' }
};

const allowed = ['item1', 'item3'];

const filtered = Object.keys(raw)
  .filter(key => allowed.includes(key))
  .reduce((obj, key) => {
    obj[key] = raw[key];
    return obj;
  }, {});

console.log(filtered);

используется:

  1. Object.keys чтобы перечислить все свойства в raw (исходные данные), затем
  2. Array.prototype.filter для выбора клавиш, присутствующих в списке разрешенных, с помощью
    1. Array.prototype.includes чтобы убедиться, что они настоящее
  3. Array.prototype.reduce чтобы построить новый объект только с разрешенными свойствами.

это сделает мелкую копию с разрешенными свойствами (но не будет копировать сами свойства).

вы также можете использовать оператор распространения объекта чтобы создать ряд объектов без их мутации (благодаря rjerue для упоминания это):

const raw = {
  item1: { key: 'sdfd', value:'sdfd' },
  item2: { key: 'sdfd', value:'sdfd' },
  item3: { key: 'sdfd', value:'sdfd' }
};

const allowed = ['item1', 'item3'];

const filtered = Object.keys(raw)
  .filter(key => allowed.includes(key))
  .reduce((obj, key) => {
    return {
      ...obj,
      [key]: raw[key]
    };
  }, {});

console.log(filtered);

в целях мелочи, если вы хотите удалить ненужные поля из исходных данных (которые я не будет рекомендую делать, так как это связано с некоторыми уродливыми мутациями), вы можете инвертировать includes проверьте так:

const raw = {
  item1: { key: 'sdfd', value:'sdfd' },
  item2: { key: 'sdfd', value:'sdfd' },
  item3: { key: 'sdfd', value:'sdfd' }
};

const allowed = ['item1', 'item3'];

Object.keys(raw)
  .filter(key => !allowed.includes(key))
  .forEach(key => delete raw[key]);

console.log(raw);

Я включаю этот пример, чтобы показать решение на основе мутаций, но я не предлагаю его использовать.

если вы в порядке с использованием синтаксиса ES6, я считаю, что самый чистый способ сделать это, как отмечалось здесь и здесь - это:

const data = {
  item1: { key: 'sdfd', value:'sdfd' },
  item2: { key: 'sdfd', value:'sdfd' },
  item3: { key: 'sdfd', value:'sdfd' }
};

const { item2, ...newData } = data;

теперь newData содержит:

{
  item1: { key: 'sdfd', value:'sdfd' },
  item3: { key: 'sdfd', value:'sdfd' }
};

или, если у вас есть ключ, хранящийся в виде строки:

const key = 'item2';
const { [key]: _, ...newData } = data;

в последнем случае, [key] превращается в item2 но так как вы используете const назначение, необходимо указать имя для назначения. _ представляет собой выбрасывать значение.

в целом:

const { item2, ...newData } = data; // Assign item2 to item2
const { item2: someVarName, ...newData } = data; // Assign item2 to someVarName
const { item2: _, ...newData } = data; // Assign item2 to _
const { ['item2']: _, ...newData } = data; // Convert string to key first, ...

это не только уменьшает вашу операцию до одного лайнера, но и не требует, чтобы вы знали, какие другие ключи (те, которые вы хотите сохранить).

самый чистый способ вы можете найти с Lodash#pick

const _ = require('lodash');

const allowed = ['item1', 'item3'];

const obj = {
  item1: { key: 'sdfd', value:'sdfd' },
  item2: { key: 'sdfd', value:'sdfd' },
  item3: { key: 'sdfd', value:'sdfd' }
}

const filteredObj = _.pick(obj, allowed)

ничего, что не было сказано раньше, но объединить некоторые ответы на общий ответ ES6:

const raw = {
  item1: { key: 'sdfd', value: 'sdfd' },
  item2: { key: 'sdfd', value: 'sdfd' },
  item3: { key: 'sdfd', value: 'sdfd' }
};

const filteredKeys = ['item1', 'item3'];

const filtered = filteredKeys
  .reduce((obj, key) => ({ ...obj, [key]: raw[key] }), {});

console.log(filtered);

вы можете добавить универсальный ofilter (реализовано с помощью generic oreduce) Так что вы можете легко фильтровать объекты, как вы можете массивы.

const oreduce = (f, acc, o)=>
  Object.keys(o).reduce((acc, k)=> f(acc, o[k], k, o), acc)

const ofilter = (f, o)=>
  oreduce ((acc, v, k, o)=>
    f(v, k, o)
      ? Object.assign(acc, {[k]: v})
      : acc, {}, o)

let data = {
  item1: { key: 'sdfd', value:'sdfd' },
  item2: { key: 'sdfd', value:'sdfd' },
  item3: { key: 'sdfd', value:'sdfd' }
}

// filter data where k ("key") is not equal to 'item2' 
let result = ofilter ((v,k)=> k !== 'item2', data)

console.log(result)

эти две функции могут быть реализованы различными способами. Я решил прикрепить к Array.prototype.reduce внутри oreduce но вы могли бы так же легко написать все это с нуля

просто еще одно решение, которое я играл с функцией "деструктурирования"

const raw = {
    item1: { key: 'sdfd', value: 'sdfd' },
    item2: { key: 'sdfd', value: 'sdfd' },
    item3: { key: 'sdfd', value: 'sdfd' }
  };
var myNewRaw = (({ item1, item3}) => ({ item1, item3 }))(raw);
console.log(myNewRaw);

кстати, я не знаю, почему я должен поставить возврат в этом случае, чтобы заставить его работать в противном случае я столкнулся с неопределенным. Самое странное-это

var myNewRaw = (({ item1, item3}) => { item1, item3})(raw); 
// myNewRaw undefined

но это ниже работает:

var myNewRaw = (({ item1, item3}) => [item1, item3] )(raw) ;
//myNewRaw ​​​​​[ { key: 'sdfd', value: 'sdfd' },​​​​​{ key: 'sdfd', value: 'sdfd' } ]​​​​​

Это, как я это сделал недавно:

const dummyObj = Object.assign({}, obj);
delete dummyObj[key];
const target = Object.assign({}, {...dummyObj});

Piggybacking on ответ ssube.

вот многоразовая версия.

Object.filterByKey = function (obj, predicate) {
  return Object.keys(obj)
    .filter(key => predicate(key))
    .reduce((out, key) => {
      out[key] = obj[key];
      return out;
    }, {});
}

чтобы назвать его использовать

const raw = {
  item1: { key: 'sdfd', value:'sdfd' },
  item2: { key: 'sdfd', value:'sdfd' },
  item3: { key: 'sdfd', value:'sdfd' }
};

const allowed = ['item1', 'item3'];

var filtered = Object.filterByKey(raw, key => 
  return allowed.includes(key));
});

console.log(filtered);

красивая вещь о функциях стрелки ES6 заключается в том, что вам не нужно проходить в allowed в качестве параметра.

ответы здесь определенно подходят, но они немного медленные, потому что они требуют циклического перебора белого списка для каждого свойства в объекте. Решение, приведенное ниже, намного быстрее для больших наборов данных, потому что оно проходит через белый список только один раз:

const data = {
  allowed1: 'blah',
  allowed2: 'blah blah',
  notAllowed: 'woah',
  superSensitiveInfo: 'whooooah',
  allowed3: 'bleh'
};

const whitelist = ['allowed1', 'allowed2', 'allowed3'];

function sanitize(data, whitelist) {
    return whitelist.reduce(
      (result, key) =>
        data[key] !== undefined
          ? Object.assign(result, { [key]: data[key] })
          : result,
      {}
    );
  }

  sanitize(data, whitelist)

Вы можете сделать что-то вроде этого:

const base = {
  item1: { key: 'sdfd', value:'sdfd' },
  item2: { key: 'sdfd', value:'sdfd' },
  item3: { key: 'sdfd', value:'sdfd' }
};

const filtered = (
    source => { 
        with(source){ 
            return {item1, item3} 
        } 
    }
)(base);

// one line
const filtered = (source => { with(source){ return {item1, item3} } })(base);

это работает, но не очень понятно, плюс with оператор не рекомендуется (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/with).

ОК как насчет этого:

const myData = {
  item1: { key: 'sdfd', value:'sdfd' },
  item2: { key: 'sdfd', value:'sdfd' },
  item3: { key: 'sdfd', value:'sdfd' }
};

function filteredObject(obj, filter) {
  if(!Array.isArray(filter)) {
   filter = [filter.toString()];
  }
  const newObj = {};
  for(i in obj) {
    if(!filter.includes(i)) {
      newObj[i] = obj[i];
    }
  }
  return newObj;
}

и назовем это так:

filteredObject(myData, ['item2']); //{item1: { key: 'sdfd', value:'sdfd' }, item3: { key: 'sdfd', value:'sdfd' }}

есть много способов сделать это. Элемент принято отвечать использует подход Keys-Filter-Reduce, который не является самым эффективным.

, С помощью for...in цикл для циклического перебора ключей объекта или перебора разрешенных ключей и затем создание нового объекта является ~50% более производительнымa.
const obj = {
  item1: { key: 'sdfd', value:'sdfd' },
  item2: { key: 'sdfd', value:'sdfd' },
  item3: { key: 'sdfd', value:'sdfd' }
};

const keys = ['item1', 'item3'];

function keysReduce (obj, keys) {
  return keys.reduce((acc, key) => {
    if(obj[key] !== undefined) {
      acc[key] = obj[key];
    }
    return acc;
  }, {});
};

function forInCompose (obj, keys) {
  const returnObj = {};
  for (const key in obj) {
    if(keys.includes(key)) {
      returnObj[key] = obj[key]
    }
  };
  return returnObj;
};

keysReduce(obj, keys);   // Faster if the list of allowed keys are short
forInCompose(obj, keys); // Faster if the number of object properties are low

а. Вижу см. Этот тест jsperf для тестов простого варианта использования. Результаты будут в зависимости от браузеров.

более простое решение без использования filter может быть достигнуто с Object.entries() вместо Object.keys()

const raw = {
  item1: { key: 'sdfd', value:'sdfd' },
  item2: { key: 'sdfd', value:'sdfd' },
  item3: { key: 'sdfd', value:'sdfd' }
};

const allowed = ['item1', 'item3'];

const filtered = Object.entries(raw).reduce((acc,elm)=>{
  const [k,v] = elm
  if (allowed.includes(k)) {
    acc[k] = v 
  }
  return acc
},{})

эта функция будет фильтровать объект на основе списка ключей, его более эффективным, чем предыдущий ответ, поскольку он не должен использовать массив.фильтр перед вызовом уменьшить. так что его O(n) в отличие от O (N + filtered)

function filterObjectByKeys (object, keys) {
  return Object.keys(object).reduce((accum, key) => {
    if (keys.includes(key)) {
      return { ...accum, [key]: object[key] }
    } else {
      return accum
    }
  }, {})
}

Comments

    Ничего не найдено.