web-dev-qa-db-fra.com

Filtre de tableau de conditions multiples Javascript

J'ai besoin d'aide pour mettre en place une recherche de tableau basée sur plusieurs conditions. De plus, toutes les conditions sont conditionnelles, ce qui signifie que je peux ou non avoir besoin de filtrer sur ces conditions. Ce que j'ai:

Tableau d'objets à filtrer:

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

Je filtre le tableau ci-dessus d'objets en fonction d'un autre tableau d'éléments mélangés. Les éléments représentent les champs de recherche suivants: 

  • "searchstring": pour rechercher dans tous les champs du tableau de données toute séquence de texte correspondante 

  • objet avec des valeurs de clé représentant le type de compte et une valeur true ou false pour une valeur indiquant s'il doit être utilisé pour filtrer 

  • date de début pour filtrer le transducteur

  • date de fin du filtrage

  • nom de la catégorie pour filtrer la catégorie sur

Le tableau contenant les conditions de recherche ressemble à ceci (mais si certains champs ne sont pas nécessaires, ils seront définis sur undefined ou sur une chaîne ou un tableau vide):

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

Quelle est la meilleure façon d'y parvenir? Ce que j'ai imaginé est une recherche séparée pour chaque élément du tableau de filtres, mais cela semble non optimal et très fastidieux. Je suis ouvert à une refonte de ma configuration ...

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

Deux notes: - changé les accolades de filtercondition pour que ce soit un tableau, mais je suggérerais d'utiliser un objet à la place . - Cet exemple {acct1:true,acct2:false,acct3:true...} n'a pas de sens pour moi, car il suggère que le champ acct devrait être acct1 et acct3 en même temps.

4
alek kowalczyk

Tout d’abord, vous voudrez utiliser des crochets pour votre tableau, pas des accolades:

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

Là encore, je ne pense pas qu'un tableau est le meilleur type de données pour cela. Essayez un objet comme celui-ci:

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

Ensuite, essayez d’utiliser Array.prototype.filter:

var filtered = data.filter(function(obj) {
    for (var key in filtercondition) {
        // if condition not met return false
    }
    return true;
});
1
Web_Designer

Créez un tableau de fonctions, chaque fonction représentant une condition.

Voici un exemple de code qui illustre l'approche ...

 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

Une fois que vous avez un tableau de conditions, vous pouvez utiliser Array.prototype.every pour exécuter chaque condition sur un élément.

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

J'allais avec un tas de petites fonctions granulaires et les composais.

//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

et un exemple de composition:

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")
        )
    ),
)

puisque je ne sais pas comment vous construisez vos conditions de filtre, je ne peux pas vous dire exactement comment les analyser dans une telle composition, mais vous pouvez les composer comme ceci:

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

etc

0
Thomas

Tout d'abord, quelques points:

  • Votre objet data n'est pas valide si vous comptez l'utiliser dans le navigateur. Les données proviennent probablement de MongoDB, non? Votre moteur (source de données) doit avoir une méthode pour le coder correctement et supprimer les références ObjectID et ISODate.

  • Votre filtercondition n'est pas un objet JavaScript/JSON valide. Vérifiez mon exemple.

Ainsi, vous pouvez filtrer votre tableau de données avec Array # filter method.

Quelque chose comme ca:

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);
0
mrlew