Search code examples
javascriptarraysreactjsjavascript-objects

Is it possible to get a list of all items in an array of objects that match items in another array?


I'm working on a React project that'll allow me to search through a list of games to help me decide what to play based on what I'm in the mood for, currently I can add games to a JSON file but I'm really struggling with the searching part.

Right now, to add a new game, you'll enter the title, genre(s) and a description of the game. The genre field is a ReduxForm FieldArray object, and I think that's what's giving me the trouble. Here's my current JSON file

{
  "games": [
    {
      "name": "Rainbow Six: Siege",
      "genres": [
        {
          "genre": "tactical"
        },
        {
          "genre": "shooter"
        }
      ],
      "description": "tactical team based shooter",
      "id": 1
    },
    {
      "name": "Resident Evil 2",
      "genres": [
        {
          "genre": "horror"
        },
        {
          "genre": "survival"
        },
        {
          "genre": "shooter"
        }
      ],
      "description": "classic resident evil 2 remake in 2019",
      "id": 2
    },
    {
      "name": "Rocket League",
      "genres": [
        {
          "genre": "cars"
        },
        {
          "genre": "competition"
        },
        {
          "genre": "one more game"
        }
      ],
      "description": "soccar!",
      "id": 3
    }
  ]
}

This is the dummy data I'm using to search:

const searchedGenres = 'horror, shooter';
const searchedList = searchedGenres.split(', ');
let foundGame = [];

Once I get the search working with this data, the plan is to allow me to just type in data on the frontend in one textbox, so "horror, shooter" would be my search term. The result from this search should only return Resident Evil 2, however I'm also receiving Rainbow Six: Siege as a result, even though it's missing one of my requested genres.

searchedList.forEach(searchedGenre => {
  this.props.games.map(game => {
    if (
      game.genres.find(
        ({ genre }) =>
          genre.toLowerCase() ===
          searchedGenre.toLowerCase().trim()
      ) !== undefined
    ) {
      foundGames.push(game);
    }
  });
});

I understand why I'm getting both Rainbow Six and Resident Evil as a result, because I'm not actually checking that both genres are in the games genres when I add the game to the foundGames array, but I'm completely lost on how I'd go about making sure all of the genres are in a game before I add it.


Solution

  • This would be a bit easier if your genres was a simple array of strings rather than objects, but still you can check pretty succinctly by leveraging some() and every() within filter() (btw filter() is a better choice than map() + push() here)

    let games = [{"name": "Rainbow Six: Siege","genres": [{"genre": "tactical"},{"genre": "shooter"}],"description": "tactical team based shooter","id": 1},{"name": "Resident Evil 2","genres": [{"genre": "horror"},{"genre": "survival"},{"genre": "shooter"}],"description": "classic resident evil 2 remake in 2019","id": 2},{"name": "Rocket League","genres": [{"genre": "cars"},{"genre": "competition"},{"genre": "one more game"}],"description": "soccar!","id": 3}]
    
    const searchedGenres = 'horror, shooter';
    const searchedList = searchedGenres.split(', ');
    
    let foundGame = games.filter(game => searchedList.every(searchItem => game.genres.some(g => g.genre == searchItem) ))
    console.log(foundGame)

    The filter condition basically says you want every game in searchedList to match at least one genre in the game. This will make it only return games that match every genre.