Мне нужна помощь, чтобы собрать поиск по массиву, который основан на нескольких условиях. Кроме того, все условия являются условными, то есть мне может или не нужно фильтровать эти условия. Что я имею:

Массив объектов для фильтрации:

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»: искать во всех полях массива данных любые согласованная текстовая последовательность

  • объект со значениями ключа, представляющими тип учетной записи и true или false для значения, указывающего, должно ли оно использоваться для фильтрации

  • startdate для фильтрации трансдейтов

  • конец, чтобы отфильтровать трансдат

  • название категории для фильтрации категории

Массив, имеющий условия поиска, выглядит следующим образом (но если некоторые поля не нужны, для них будет задано значение undefined или просто пустая строка или массив):

var filtercondition = {
    "p",
    {acct1:true,acct2:false,acct3:true...}
    "2016-06-01",
    "2016-11-30",
    "category3"
}

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

6
mo_maat 13 Янв 2017 в 05:05

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 одновременно.

5
mo_maat 16 Янв 2017 в 03:56

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

//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 = b => a => 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
    );
}

И так далее

1
Thomas 30 Дек 2019 в 17:51

Создайте массив функций, каждая функция представляет условие.

Вот пример кода, который демонстрирует подход ...

 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 etc

Если у вас есть массив условий, вы можете использовать Array.prototype.every, чтобы выполнить каждое условие для элемента.

 var itemsMatchingCondition = data.filter(function(d) {
     return conditions.every(function(c) {
         return c(d);
     });
 });
1
Andrew Shepherd 13 Янв 2017 в 02:28

Во-первых, вы хотите использовать скобки для массива, а не фигурные скобки:

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"
};

Затем попробуйте использовать Array.prototype.filter:

var filtered = data.filter(function(obj) {
    for (var key in filtercondition) {
        // if condition not met return false
    }
    return true;
});
3
Web_Designer 13 Янв 2017 в 02:18

Сначала несколько моментов:

  • Ваш data объект недействителен, если вы собираетесь использовать его в браузере. Вероятно, данные получены из MongoDB, верно? Ваш бэкэнд (источник данных) должен иметь метод для его правильного кодирования и удаления ссылок ObjectID и ISODate.

  • Ваш filtercondition не является допустимым объектом JavaScript / JSON. Проверьте мой пример.

Таким образом, вы можете отфильтровать массив данных с помощью массива #filter метод.

Что-то такое:

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);
1
mrlew 13 Янв 2017 в 12:26