Filter list based on more than one field

2019-02-25 04:33发布

问题:

I'm iterating a list of jobs and there's a search implemented on this list. Search is working but now it only filters list based on one field.

Here's my list:

<ion-card *ngFor="let job of allJobs | search : searchTerm">
    <ion-grid>
    <ion-row>
        <ion-col>
        <div>
            <span> {{job.day | uppercase}}</span>
            <span> {{job.month | uppercase}}</span>
        </div>
        </ion-col>

        <ion-col>
        <div>
            <span>{{job.time}}</span>
            <span>{{job.name}}</span>
        </div>
        </ion-col>
    </ion-row>
    </ion-grid>
</ion-card>

I made a pipe for implementing search. Here's the code for it.

  transform(items: any[], terms: string): any[] {
    if(!items) return [];
    if(!terms) return items;
    terms = terms.toLowerCase();
    return items.filter( it => {
      return it.name.toLowerCase().includes(terms); // only filter name
    });
  }

Now the list gets filtered only based on the name field. I wanna filter the list based on day, month and time as well.

Can anyone tell me how to make this happen?

Sample Data for Jobs. Jobs is an array of objects

   [  
      {  
         "id":10,
         "day":"Monday",
         "month":"June",
         "time":"10",
         "name":"John",
         "email":"john@gmail.com"
      },
      {  
         "id":11,
         "day":"Tuesday",
         "month":"May",
         "time":"12",
         "name":"Jane",
         "email":"jane@gmail.com"
      },
      {  
         "id":12,
         "day":"Friday",
         "month":"June",
         "time":"16",
         "name":"",
         "email":"john@gmail.com"
      },
      {  
         "id":13,
         "day":"Tuesday",
         "month":"August",
         "time":"21",
         "name":"",
         "email":"kevin@gmail.com"
      },
      {  
         "id":14,
         "day":"Saturday",
         "month":"December",
         "time":"12",
         "name":"Sam",
         "email":"sam@gmail.com"
      },

   ]

And searchTerm is just a string.

As you can see, there are more fields in the sample data than the one displayed in the HTML but I'm trying only to search for the fields that are displayed in the HTML. Some fields can have null values (for eg. name in the sample data has two null values)

I tried the solutions already provided but none of them are working for my requirement.

P.S: Read somewhere that pipes are not the best option to do functionality like this. I'm ready to implement this logic in the class as well.

回答1:

Try this code.. it's pretty simple.

  transform(items: any[], terms: string): any[] {
    if (!items) return [];
    if (!terms) return items;
    terms = terms.toLowerCase();
    terms = terms.trim();
    return items.filter(it => {
      if (it.day) {
        return it.day.toLowerCase().includes(terms);
      }
      if (it.month) {
        return it.month.toLowerCase().includes(terms);
      }
      if (it.time) {
        return it.time.toLowerCase().includes(terms);
      }
      if (it.name) {
        return it.name.toLowerCase().includes(terms);
      }
    });
  }

If your JSON has null values, you can replace it with an empty string using the following code:

items = JSON.parse(JSON.stringify(items).replace(/null/g, '""'));


回答2:

Try combining your includes with the logical or-operator (||):

transform(items: any[], terms: string): any[] {
    if (!items) return [];
    if (!terms) return items;
    terms = terms.toLowerCase();
    return items.filter(it => {
      return it.name.toLowerCase().includes(terms) ||
        it.day.toLowerCase().includes(terms) ||
        it.month.toLowerCase().includes(terms) ||
        it.time.toLowerCase().includes(terms)
    });
}

This statement will return true if any of the includes returns true. So basically any item which name, day, month or time contains the searchterm will be returned by the pipe.

This solution assumes that name, day, month and time are not null or undefined. But I'm assuming that is okay as your sample data suggests null values will be empty strings(""). If my assumption is not correct you'll have to check if the values are assigned, before accessing them.



回答3:

This dont work because the includes dont test for multiple term cases. You didnt say whats inside the items Array but if it is a String you could do this:

transform(items: any[], terms: string): any[] {
    if(!items) return [];
    if(!terms) return items;
    terms = terms.toLowerCase();
    return items.filter( it => {
      //if it is something like "Programmer 07 November 12:00PM"
      var informations = it.split(' '); //["Programmer", "07" ,"November" ,"12:00PM"]
      var termArray = terms.split(' ');
      var rightResult = true;
      for (var index in termArray) {
       if !(informations.include(termArray[index])) {
         rightResult = false;
       }
       return rightResult;


    });
  }


回答4:

Inside your transform method of your search pipe, apply filters on all the fields you want to apply filter on. Following will search for all keys in the object:

transform(items: any[], terms: string): any[] {
    if(!items) return [];
    if(!terms) return items;
    terms = terms.toLowerCase();
    return items.filter( it => {

       return keys(it).reduce((prev, key) => {
          return prev || key.toLowerCase().includes(term);
       }, false);
    });
}

If keys or Object.keys are not working, use the following code instead of reduce function:

...
let bInclude = false;
for(let key in it){
  bInclude = bInclude || key.toLowerCase().includes(term);
}

return bInclude;
...