Фильтр массива множественных условий Javascript
Мне нужна помощь в создании поиска по массиву, основанного на нескольких условиях. Кроме того, все условия являются условными, что означает, что я могу или не должен фильтровать по этим условиям. Что у меня есть:
Массив объектов для фильтрации:
var data = [{
"_id" : ObjectId("583f6e6d14c8042dd7c979e6"),
"transid" : 1,
"acct" : "acct1",
"transdate" : ISODate("2012-01-31T05:00:00.000Z"),
"category" : "category1",
"amount" : 103
},
{
"_id" : ObjectId("583f6e6d14c8042dd7c2132t6"),
"transid" : 2,
"acct" : "acct2",
"transdate" : ISODate("2012-01-31T05:00:00.000Z"),
"category" : "category2",
"amount" : 103
},
{
"_id" : ObjectId("583f6e6d14c8042dd7c2132t6"),
"transid" : 3,
"acct" : "acct2",
"transdate" : ISODate("2016-07-31T05:00:00.000Z"),
"category" : "category1",
"amount" : 103
},
{
"_id" : ObjectId("583f6e6d14c8042dd7c2132t6"),
"transid" : 4,
"acct" : "acct2",
"transdate" : ISODate("2012-01-31T05:00:00.000Z"),
"category" : "category2",
"amount" : 103
},
{
"_id" : ObjectId("583f6e6d14c8042dd7c2132t6"),
"transid" : 5,
"acct" : "acct2",
"transdate" : ISODate("2012-01-31T05:00:00.000Z"),
"category" : "category3",
"amount" : 103
},
{
"_id" : ObjectId("583f6e6d14c8042dd7c152g2"),
"transid" : 6,
"acct" : "acct3",
"transdate" : ISODate("2016-10-31T05:00:00.000Z"),
"category" : "category3",
"amount" : 103
}]
Я фильтрую вышеупомянутый массив объектов на основе другого массива смешанных элементов. Элементы представляют собой следующие поля поиска:
"searchstring": поиск по всем полям в массиве данных для какой-нибудь
согласованная текстовая последовательность
Объект с ключевыми значениями reprsenting account type и true или false
для значения, указывающего, следует ли его использовать для фильтрации
Startdate для фильтрации transdate на
Enddate для фильтрации transdate
Название категории для фильтрации категории по
Массив, содержащий условия поиска, выглядит следующим образом (но если некоторые из полей не являются необходимыми, они будут установлены в undefined или просто an пустая строка или массив):
var filtercondition = {
"p",
{acct1:true,acct2:false,acct3:true...}
"2016-06-01",
"2016-11-30",
"category3"
}
Как лучше всего это сделать? То, что я придумал, - это отдельный поиск для каждого элемента в массиве фильтров, но это кажется неоптимальным и очень утомительным. Я открыт для изменения дизайна моей установки...
5 ответов:
// You wrote that it's an array, so changed the braces var filtercondition = ["p", {acct1:true,acct2:false,acct3:true...} "2016-06-01", "2016-11-30", "category3" ]; var filtered = data.filter(o => { if(filtercondition[0] && !o.category.includes(filtercondition[o])) { // checking just the category, but you can check if any of more fields contains the conditions return false; } if(filtercondition[1]) { for(var key in filtercondition[1]) { if(filtercondition[1][key] === true && o.acct != key) { return false; } } } if(filtercondition[2] && o.transdate < filtercondition[2]) { return false; } if(filtercondition[3] && o.transdate > filtercondition[3]) { return false; } if(filtercondition[4] && o.category !== filtercondition[4]) { return false; } return true; });Две ноты: - изменил скобки
filterconditionтак, что это массив, однако я бы предложил использовать объект вместо этого. - этот пример{acct1:true,acct2:false,acct3:true...}не имеет для меня смысла, так как он предполагает, что полеacctдолжно бытьacct1иacct3одновременно.
Во-первых, вы захотите использовать скобки для вашего массива, а не фигурные скобки:
var filtercondition = [ "p", {acct1:true,acct2:false,acct3:true...}, "2016-06-01", "2016-11-30", "category3" ];Опять же, я не думаю, что массив является лучшим типом данных для этого. Попробуйте такой объект:
var filtercondition = { query: "p", accounts: {acct1:true,acct2:false,acct3:true...}, date1: "2016-06-01", date2: "2016-11-30", category: "category3" };Затем попробуйте использовать массив.прототип.фильтр:
var filtered = data.filter(function(obj) { for (var key in filtercondition) { // if condition not met return false } return true; });
Создайте массив функций, каждая из которых представляет условие.
Вот пример кода, который демонстрирует этот подход...
Если у вас есть массив условий, вы можете использовать массив .прототип.каждый для выполнения каждого условия на элементе.var conditions = []; // Dynamically build the list of conditions if(startDateFilter) { conditions.push(function(item) { return item.transdate >= startDateFilter.startDate; }); }; if(categoryFilter) { conditions.push(function(item) { return item.cateogry === categoryFilter.category; }); }; // etc etcvar itemsMatchingCondition = data.filter(function(d) { return conditions.every(function(c) { return c(d); }); });
Я бы пошел с кучей мелких гранулярных функций и составил их.
//only some utilities, from the top of my mind var identity = v => v; //string-related var string = v => v == null? "": String(v); var startsWith = needle => haystack => string(haystack).startsWith(needle); var endsWith = needle => haystack => string(haystack).endsWith(needle); var contains = needle => haystack => string(haystack).contains(needle); //do sth with an object var prop = key => obj => obj != null && prop in obj? obj[prop]: undefined; var someProp = fn => obj => obj != null && Object.keys(obj).some(k => fn(k) ); var someValue = fn => obj => obj != null && Object.keys(obj).some(k => fn(obj[k]) ); //logic var eq => a => b => a === b; var not => fn => function(){ return fn.apply(this, arguments) }; var and = (...funcs) => funcs.reduce((a, b) => function(){ return a.apply(this, arguments) && b.apply(this, arguments); }); var or = (...funcs) => funcs.reduce((a, b) => function(){ return a.apply(this, arguments) || b.apply(this, arguments); }); //composition var compose = (...funcs) => funcs.reduce((a, b) => v => return a(b(v))); var chain = (...funcs) => funcs.reduceRight((a, b) => v => return a(b(v))); //and whatever else you want/need //but stay granular, don't put too much logic into a single functionИ пример композиции:
var filterFn = and( //some value contains "p" someValue(contains("p")), //and chain( //property "foo" prop("foo"), or( //either contains "asdf" contains("asdf"), //or startsWith "123" startsWith("123") ) ), )Поскольку я не знаю, как вы строите свои условия фильтрации, я не могу точно сказать вам, как их разбирать в такую композицию, но вы могли бы составить их следующим образом:
//start with something basic, so we don't ever have to check wether filterFn is null var filterFn = identity; //and extend/compose it depending on some conditions if(/*hasQuery*/){ filterFn = and( // previous filterFn(obj) && some value on obj contains `query` filterFn, someValue(contains(query))) ) } if(/*condition*/){ //extend filterFn filterFn = or( // (obj.foo === null) || previous filterFn(obj) chain(prop("foo"), eq(null)), filterFn ); }И так далее
Во-первых, некоторые моменты:
Таким образом, вы можете фильтровать массив данных с помощью метода Array#filter.
Ваш объект
dataнедопустим, если вы собираетесь использовать его в браузере. Вероятно, данные поступают изMongoDB, верно? Ваш сервер (источник данных) должен иметь метод для его правильного кодирования и удаления ссылокObjectIDиISODate.Ваш
filterconditionне является допустимым объектом JavaScript / JSON. Проверьте мой пример.Что-то вроде это:
let data = [{ "_id" : "583f6e6d14c8042dd7c979e6", "transid" : 1, "acct" : "acct1", "transdate" : "2012-01-31T05:00:00.000Z", "category" : "category1", "amount" : 103 }, { "_id" : "583f6e6d14c8042dd7c2132t6", "transid" : 2, "acct" : "acct2", "transdate" : "2012-01-31T05:00:00.000Z", "category" : "category2", "amount" : 103 }, { "_id" : "583f6e6d14c8042dd7c2132t6", "transid" : 5, "acct" : "acct2", "transdate" : "2012-01-31T05:00:00.000Z", "category" : "category3", "amount" : 103 }]; let filterToApply = { acct: { acct1: true, acct2: false, acct3: true }, initialDate: "2016-06-01", finalDate: "2016-11-30", category: "category3" } let filterData = (array, filter) => { return array.filter( (item) => { /* here, you iterate each item and compare with your filter, if the item pass, you must return true. Otherwise, false */ /* e.g.: category check (if present only) */ if (filter.category && filter.category !== item.category) return false; } /* add other criterias check... */ return true; }); } let dataFiltered = filterData(data, filterToApply); console.log(dataFiltered);
Comments