0

I am working on a photo gallery for swimming pools. The search bar updates state per character entered, similar to the Netflix search bar. It updates on all keys in the JSON file, similar to how you can search for either "Good Will Hunting" as a title or "Matt Damon" as an actor on Netflix.

The issue I am running into is that "round" will be a common search term, triggering "shape":"round" in the JSON. But another category is "type": "above-ground" or "type":"in-ground".

As a result, if you search for a round pool, the query doesn't filter to show only round pools because "round" is also triggering the word "ground". It shows a mish-mash of round pools and any pool that triggers "above-ground" or "in-ground".

I have tried messing around with the .filter and .includes methods to no avail. I'm not sure if I'm missing something there or what.

I'm also about to look into regular expressions to maybe dictate "if the search term "round" does not have a preceding character, show round pools and ignore the ground trigger".

import React, { Component } from "react";

class App extends Component {
  state = {
    filter: "",
    data: [
      {
        shape: "Round",
        type: "Above-ground"
      },
      {
        shape: "Rectangle",
        type: "In-ground"
      },
      {
        shape: "Kidney",
        type: "In-ground"
      }
    ]
  };

  handleChange = event => {
    this.setState({
      filter: event.target.value
    });
  };

  render() {
    const { filter, data } = this.state;
    const lowerCasedFilter = filter.toLowerCase();
    const filteredData = data.filter(item => {
      return Object.keys(item).some(key =>
        item[key].toLowerCase().includes(lowerCasedFilter)
      );
    });

    return (
      <div>
        <input value={filter} onChange={this.handleChange} />
        {filteredData.map(item => (
          <div>
            <p>Shape: {item.shape}</p>
            <p>Type: {item.type}</p>
            <hr />
          </div>
        ))}
      </div>
    );
  }
}

export default App;

I expect for the search filter to only show "round" pools when the user enters "round", and not show excess results due to the word "ground"

3 Answers3

1

find if one word starts like lowerCasedFilter in any field of data objects

item[key].toLowerCase().split(' ').some(word => word.slice(0, lowerCasedFilter.length) === lowerCasedFilter)

  const data = [
    {
      shape: "Round",
      type: "Above-ground"
    },
    {
      shape: "Rectangle",
      type: "In-ground"
    },
    {
      shape: "Kidney",
      type: "In-ground"
    }
  ]


filteredData = (lowerCasedFilter) => data.filter(item => {
  return Object.keys(item).some(key =>
    item[key].toLowerCase().split(' ').some(word => word.slice(0, lowerCasedFilter.length) === lowerCasedFilter)
  );
});

console.log( filteredData('round') )
1

You could utilize a dynamic RegExp that uses word boundaries \b for "complete word" matches.

const lowerCasedFilter = filter.toLowerCase();
const filteredData = data.filter(item => {
  return Object.keys(item).some(key => {
    const stringToSearch = item[key].toLowerCase();
    const regex = new RegExp("\\b"+ lowerCasedFilter +"\\b"); // build regex around the given search term ("round" in your case)
    return regex.test(stringToSearch); // use "test" to see if your term is present in the property value
  });
});

Relevant information in this post: whole word match in javascript.

SteamDev
  • 4,294
  • 5
  • 20
  • 29
0

If it's specific to the five letters "round", you could just use

const filteredData = lowerCasedFilter === "round" ? 
  data.filter(item => item.shape === "Round") : 
  data.filter(item => {
    return Object.keys(item).some(key =>
      item[key].toLowerCase().includes(lowerCasedFilter)
    );
  });
Heretic Monkey
  • 11,687
  • 7
  • 53
  • 122