Фильтровать свойства объекта по ключу в 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, поэтому операторы распространения доступны для меня.
Спасибо!
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);используется:
Object.keysчтобы перечислить все свойства вraw(исходные данные), затемArray.prototype.filterдля выбора клавиш, присутствующих в списке разрешенных, с помощью
Array.prototype.includesчтобы убедиться, что они настоящее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(реализовано с помощью genericoreduce) Так что вы можете легко фильтровать объекты, как вы можете массивы.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