Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javascript multiple condition array filter

I need help putting together an array search that is based on multiple conditions. Furthermore, all the conditions are conditional, meaning I may or may not need to filter on those conditions. What I have:

Array of objects to filter:

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
}]

I am filtering the above array of objects based on another array of mixed elements. The elements represent the following search fields:

  • "searchstring": to search on all fields in the data array for any matched text sequence

  • object with key values reprsenting account type and a true or false for value indicating if it should be used to filter

  • startdate to filter transdate on

  • enddate to filter transdate

  • category name to filter category on

The array that has the search conditions looks like this (but if some of the fields are not necessary they will be set to undefined or just an empty string or array):

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

What is the best way to accomplish this? What I've devised is a separate search for each element in the filter array, but this seems non optimal and very tedious. I'm open to a redesign of my setup...

like image 300
mo_maat Avatar asked Jan 13 '17 02:01

mo_maat


3 Answers

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

Two notes: - changed the braces of filtercondition so that it is an array, however I would suggest to use an object instead. - this {acct1:true,acct2:false,acct3:true...} sample doesn't make sense for me, since it suggests that the acct field should be acct1 and acct3 at the same time.

like image 134
alek kowalczyk Avatar answered Oct 02 '22 04:10

alek kowalczyk


Create an array of functions, each function representing a condition.

Here's some sample code which demonstrates the approach...

 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

Once you have an array of conditions, you can use Array.prototype.every to run each condition on an item.

 var itemsMatchingCondition = data.filter(function(d) {
     return conditions.every(function(c) {
         return c(d);
     });
 });

Or, using the more compact arrow functions:

const itemsMatchingCondition = data.filter(d => conditions.every(c => c(d));
like image 33
Andrew Shepherd Avatar answered Oct 02 '22 03:10

Andrew Shepherd


First, you'll want to use brackets for your array not curly braces:

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

Then again, I don't think that an array is the best data type for that. Try an object like this:

var filtercondition = {
    query: "p",
    accounts: {acct1:true,acct2:false,acct3:true...},
    date1: "2016-06-01",
    date2: "2016-11-30",
    category: "category3"
};

Then, try using Array.prototype.filter:

var filtered = data.filter(function(obj) {
    for (var key in filtercondition) {
        // if condition not met return false
    }
    return true;
});
like image 30
Web_Designer Avatar answered Oct 02 '22 02:10

Web_Designer