Nikhil Bhandarkar
Nikhil Bhandarkar

Reputation: 709

Angular search filter pipe for full sentence search irrespective of word order

I want to make a search bar which will search 'title' from the table below, irrespective of the order of the words in that sentence.

I tried implementing a filter pipe which checks if the search string is present in the title. I also tried splitting both strings in the array and looping through one to check common fields.

Basically, I want the same results on both 'Samsung Metro' and 'Metro Samsung'

Below is the link to the GIF of the working product and issue.

https://user-images.githubusercontent.com/26569942/56949597-f8f6f280-6b50-11e9-8521-bfd1235126d9.gif

Thanks for the help.

///////////////////////
//home.component.html//
///////////////////////

<div class="form-group">
  <input type="text" [(ngModel)]="searchTerm">  
</div>
<div class="row">

  <table class="table table-striped col-12" >
    <thead>
      <tr>
        <th scope="col">Title</th>
        <th scope="col">Price</th>
        <th scope="col">Popularity</th>
        <th scope="col">Subcategory</th>
      </tr>
    </thead>
    <tbody>
      <tr *ngFor="let item of products | keyvalue | productFilter : searchTerm">
        <th scope="row">{{item.value.title}}</th>
        <td>{{item.value.price}}</td>
        <td>{{item.value.popularity}}</td>
        <td>{{item.value.subcategory}}</td>
      </tr>
    </tbody>
  </table>

</div>
/////////////////////
//home.component.ts//
/////////////////////

export class HomeComponent implements OnInit {

  jsonData: any;
  products: any;
  count: number;
  searchTerm: string;

  constructor(private data: DataService) { }

  ngOnInit() {
    this.data.getData().subscribe(
      (t: any) => {
        this.products = t.products;
        this.count = t.count;
      }
    );
  }
}
//////////////////////////
//product-filter.pipe.ts//
//////////////////////////

import { PipeTransform, Pipe } from '@angular/core';

@Pipe({
    name: 'productFilter'
})

export class ProductFilterPipe {
    transform(products: any[], searchTerm: string) {

        if (!products || !searchTerm) {
            return products;
        }

        return products.filter(function (product) {

            return product.value.title.toLowerCase().includes(searchTerm.toLowerCase());

            // Below commented code is my failed try
            // product.value.title.toLowerCase().split(" ").filter( item => {
            //     searchTerm.toLowerCase().split(" ").includes(item);
            // })

        });
    }
}
////////////////////////
//data looks like this//
////////////////////////
{
    "count": 3,
    "products": {
      "6834": {
        "subcategory": "mobile",
        "title": "Micromax Canvas Spark",
        "price": "4999",
        "popularity": "51936"
      },
      "5530": {
        "subcategory": "mobile",
        "title": "Samsung Galaxy Grand Max",
        "price": "12950",
        "popularity": "48876"
      },
      "4340": {
        "subcategory": "mobile",
        "title": "Apple iPhone 6",
        "price": "40999",
        "popularity": "46198"
      }
    }
}

Upvotes: 2

Views: 1558

Answers (2)

Nikhil Bhandarkar
Nikhil Bhandarkar

Reputation: 709

Figured it out something like this.

import { PipeTransform, Pipe } from '@angular/core';

@Pipe({
    name: 'productFilter'
})

export class ProductFilterPipe {
    transform(products: any[], searchTerm: string) {

        if (!products || !searchTerm) {
            return products;
        }

        return ProductFilterPipe.filter(products, searchTerm);
    }

    static filter(products: Array<{ [key: string]: any }>, searchTerm: string): Array<{ [key: string]: any }> {

        return products.filter(function (product: any) {
            return searchTerm.trim().toLowerCase().split(" ").some(r=> product.value.title.toLowerCase().indexOf(r) >= 0)
        });
    }
}

Upvotes: 1

Markus Dresch
Markus Dresch

Reputation: 5584

You can create a regular expression from your search string and test it against your data:

new RegExp(['Galaxy', 'Samsung'].map(s => `(?=.*?${s})`).join('') + '.*').test('Samsung Galaxy');

So in your case, split the search term and test product.value.title:

new RegExp(searchTerm.split(' ').map(s => `(?=.*?${s})`).join('') + '.*').test(product.value.title);

Upvotes: 1

Related Questions