Skip to content

Instantly share code, notes, and snippets.

@brokenthorn
Created July 21, 2021 16:03
Show Gist options
  • Save brokenthorn/881507cb59c8cb7854d637c865fd376c to your computer and use it in GitHub Desktop.
Save brokenthorn/881507cb59c8cb7854d637c865fd376c to your computer and use it in GitHub Desktop.
Angular Pipe Snippet for Recursive Filter Object Properties By Term with optional limit to a single property
import {Injectable, Pipe, PipeTransform} from '@angular/core';
@Pipe({
name: 'filterBy',
pure: true
})
@Injectable()
export class FilterByPipe implements PipeTransform {
/**
* An array filter function.
*
* If `item` is a string, this function returns true if it includes the search term `term`.
*
* If `item` is an object, this function is called recursively to traverse that object until it finds a string
* property that contains the search term `term` and it returns true in that case, otherwise it returns false.
*
* If `onlyProperty` is given and `item` has that property, this function either only traverses that property of
* `item` if `item[onlyProperty]` is an object, or only checks if `term` is included in that property if it is a
* string.
*
* If `onlyProperty` is given and `item` does not contain that property, this function returns false immediately.
*
* @param item The input item to traverse and check if it includes `term`.
* @param term The search term.
* @param onlyProperty An optional single property of `item` to traverse and check if it includes `term`.
* @param caseSensitive If set to true, will perform case-sensitive comparisons.
*/
static filter(item: any,
term: string,
caseSensitive: boolean = false,
onlyProperty: string | null = null): boolean {
if (term === null || term.length === 0) {
return true;
}
// FIXME: with very large objects, each recursion of this function will allocate a new instance of this variable.
const searchTerm = caseSensitive ? term : term.toLowerCase();
if (typeof item === 'string') {
const itemAsString = caseSensitive ? item as string : (item as string).toLowerCase();
return itemAsString.includes(searchTerm);
}
if (typeof item === 'object') {
// short-circuit: if onlyProperty is given, return the result of a recursive call that only goes down the tree
// starting at the object at item[onlyProperty]:
if (onlyProperty !== null && onlyProperty.length > 0) {
if (!item.hasOwnProperty(onlyProperty)) {
return false;
}
const propertyValue = item[onlyProperty];
return FilterByPipe.filter(propertyValue, searchTerm, caseSensitive);
}
for (const propertyName in item) {
if (item.hasOwnProperty(propertyName)) {
const propertyValue = item[propertyName];
if (propertyValue === null || propertyValue === undefined) {
continue;
}
if (FilterByPipe.filter(propertyValue, searchTerm, caseSensitive)) {
return true;
}
}
}
}
return false;
}
// TODO: Add support for string[] or maybe even number[] input items
transform(items: Array<{ [key: string]: any }>,
searchTerm: string,
caseSensitive: boolean = false,
property: string | null = null): Array<{ [key: string]: any }> {
if (!items || !searchTerm) {
return items;
}
return items.filter(item => FilterByPipe.filter(item, searchTerm, caseSensitive, property));
}
}
@brokenthorn
Copy link
Author

Used like so:

<ion-toolbar>
  <ion-searchbar placeholder="Search..." [(ngModel)]="searchTerm"></ion-searchbar>
</ion-toolbar>

<ion-list inset="true">
  <ion-item *ngFor="let i of items | filterBy:searchTerm" [routerLink]="['./',i.id]">
    <ion-label>{{i.name}}</ion-label>
  </ion-item>
</ion-list>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment