Search code examples
javascriptarraysangularngrx

filtering multiple arrays ngrx


I have a store setup that has multiple arrays

I'm trying to search all arrays at once, via a textfield.

I can get this done, by calling a selector function on keyup, that filters the 4 arrays and pushes to a new array.

I've thought about merging all the arrays to one array before filtering, but I want to keep the results separate, as they are going to be displayed in categories.

Just trying to see if I can streamline the performance at all and if there's a more concise way of doing this, in case I need to do something similar with larger arrays.

my textField function:

this.renderer.listen(this.serachField.nativeElement, 'keyup', (event) => {
      if (this.serachField.nativeElement.value.length < 3) { return; }
      this.store.pipe(select(search(this.serachField.nativeElement.value)),take(1))
      .subscribe(data => {
          console.log(data);
        });
    })

The selector function:

export const search = (searchString: string): any => {
    return createSelector(
             appState,
             (state: AppState) => {
                 let arr: any = [];
                 let s = searchString.toUpperCase();
                const comics = state.comics.results.filter((item: Comic) => {
                    let title = item.title.toUpperCase();
                    console.log(title);
                    return title.includes(s);

                });

                const music = state.music.items.filter((item: Album) => {
                    let title = item.name.toUpperCase();
                    console.log(title);
                    return title.includes(s);

                });

                const movies = state.movies.results.filter((item: Movie) => {
                    let title = item.title.toUpperCase();
                    console.log(title);
                    return title.includes(s);

                });

                const games = state.games.filter((item: Game) => {
                    let title = item.title.toUpperCase();
                    console.log(title);
                    return title.includes(s);

                });
                arr.push(comics, music, movies, games);
                return arr;
             }
         );

 };

EDIT: After @GustavMH correct answer I had to slightly change the code to be a little more dynamic in terms of the array naming as follows

export const search = (searchString: string): any => {
    return createSelector(
             appState,
             (state: any) => {
                 let s = searchString.toUpperCase();
                 const keys = [{state: "comics", item: "title", array: 'results'},
                               {state: "music", item: "name", array: 'items'},
                               {state: "movies", item: "title", array: 'results'},
                               {state: "games", item: "title", array: ''}]

                 return keys.map((key) => {
                 let arr = key.array ? state[key.state][key.array] : state[key.state];
                return arr.filter((item: any) => {
                     const title = item[key.item].toUpperCase();
                     console.log(item);
                     return title.includes(s);
                 })})
             }
         );

 };

Solution

  • This should implement the selector function with less code and make it more adaptable to kinds of data, if needed you can specify a more precise type in the filter function.

    export const search = (searchString: string): any => {
        return createSelector(
                 appState,
                 (state: AppState) => {
                     let s = searchString.toUpperCase();
                     const keys = [{state: "comics", item: "title"},
                                   {state: "music", item: "name"},
                                   {state: "movies", item: "title"},
                                   {state: "games", item: "title"}]
    
                     return keys.map(key => state[key.state].results.filter((item: any) => {
                         const title = item[key.item].toUpperCase();
                         console.log(title);
                         return title.includes(s);
                     }))
                 }
             );
    
     };