Search and Filter List Records

This is a simple example to list Json records in tabular format. Filter and Sort data based on user selection. There is no back-end server only need to filter and sort data based on pre-defined data-set.


Prepare Project for Search and Filter Example

To create the example project for this example, open command prompt, navigate to a convenient location, and run the command as shown below :

create-react-app example11

src\App.js
import React, { Component } from 'react';
import SearchFilter from './SearchFilter';
import "./App.css";

class App extends Component {
  render() {
    return <SearchFilter />
  }
}

export default App;

src\SearchFilter.js
import React, { Component } from "react";
import data from "./data.json";

class SearchFilter extends Component {
  state = {
    itemsToDisplay: [],
    itemsToUse: [],
    cuisines: []
  };
  render() {
    return (
      <div>
        <div className="restfilter">
          <div>
            Choose a cuisine : &nbsp;
            <select id="restfilter" onChange={this.optionSelected}>
              <option value="any">Choose Any</option>
              {this.state.cuisines.map(cuisine => {
                return <option value={cuisine}>{cuisine}</option>;
              })}
            </select>
          </div>
          <div>
            Sort by : &nbsp;
            <select id="sortfilter" onChange={this.sortBy}>
              <option value="ranking">Ranking</option>
              <option value="asc">Rating: Low to High</option>
              <option value="des">Rating: High to Low</option>
            </select>
          </div>
        </div>
        <div className="restcontainer">
          {this.state.itemsToDisplay.map(rest => {
            let cuisines = rest["Cuisine Style"]
              .substring(1, rest["Cuisine Style"].length - 2)
              .split(",");
            return (
              <div className="rest">
                <div className="restinfo">
                  <i
                    className="fas fa-map-marker"
                    style={{ color: "orangered", fontSize: "12px" }}
                  ></i>
                  &nbsp;
                  <span className="restcity">{rest["City"]}</span>
                  <br />
                  <span className="restname">{rest["Name"]}</span>
                  <div className="restcuisines">
                    {cuisines.map(cuisine => {
                      let cuisineToShow = cuisine.substring(
                        1,
                        cuisine.length - 1
                      );
                      cuisineToShow = cuisineToShow.includes("'")
                        ? cuisineToShow.substring(1, cuisineToShow.length)
                        : cuisineToShow;
                      return (
                        <div pill className="restcuisine" variant="light">
                          {cuisineToShow}
                        </div>
                      );
                    })}
                  </div>
                </div>
                <div className="sepline"></div>
                <div className="reststats">
                  <div>
                    <i
                      style={{ fontSize: "15px" }}
                      className="far fa-comment-alt"
                    ></i>
                    &nbsp;
                    {rest["Number of Reviews"]}
                  </div>
                  <div>
                    <i style={{ fontSize: "15px" }} className="far fa-star"></i>
                    &nbsp;
                    {rest["Rating"]}
                  </div>
                </div>
              </div>
            );
          })}
        </div>
      </div>
    );
  }

  filterOnSearch = event => {
    if (
      !event.target.value ||
      event.target.value === " " ||
      event.target.value === ""
    )
      this.setState({ itemsToDisplay: [...this.state.itemsToUse] });
    else {
      let itemsToDisplay = [];
      itemsToDisplay = this.state.itemsToUse.filter(
        item =>
          item["Name"]
            .toLowerCase()
            .includes(event.target.value.toLowerCase()) ||
          item["Cuisine Style"]
            .toLowerCase()
            .includes(event.target.value.toLowerCase()) ||
          item["City"].toLowerCase().includes(event.target.value.toLowerCase())
      );
      this.setState({ itemsToDisplay });
    }
  };

  optionSelected = () => {
    var e = document.getElementById("restfilter");
    var selected = e.options[e.selectedIndex].text;

    if (selected === "Choose Any")
      this.setState({ itemsToDisplay: [...this.state.itemsToUse] });
    else {
      let itemsToDisplay = [];
      itemsToDisplay = this.state.itemsToUse.filter(item =>
        item["Cuisine Style"].toLowerCase().includes(selected.toLowerCase())
      );
      this.setState({ itemsToDisplay });
    }
  };

  sortBy = () => {
    var e = document.getElementById("sortfilter");
    var selected = e.options[e.selectedIndex].value;

    if (selected === "ranking")
      this.setState({ itemsToDisplay: [...this.state.itemsToUse] });
    else if (selected === "asc") {
      let itemsToDisplay = [...this.state.itemsToDisplay];
      itemsToDisplay.sort(function(a, b) {
        return a["Rating"] - b["Rating"];
      });
      this.setState({ itemsToDisplay });
    } else {
      let itemsToDisplay = [...this.state.itemsToDisplay];
      itemsToDisplay.sort(function(a, b) {
        return b["Rating"] - a["Rating"];
      });
      this.setState({ itemsToDisplay });
    }
  };

  componentDidMount() {
    this.reRenderList();
  }

  reRenderList() {
    var cuisines = [];
    var itemsToDisplay = [];
    for (var i = 0; i < data.length; i++) {
      itemsToDisplay.push(data[i]);
      data[i]["Cuisine Style"]
        .substring(1, data[i]["Cuisine Style"].length - 2)
        .split(",")
        .forEach(cuisine => {
          let c = cuisine.substring(1, cuisine.length - 1);
          c = c.includes("'") ? c.substring(1, c.length) : c;
          if (cuisines.indexOf(c) < 0) {
            cuisines.push(c);
          }
        });
    }

    this.setState({ cuisines });

    this.setState({ itemsToDisplay }, () => {
      this.setState({ itemsToUse: [...this.state.itemsToDisplay] });
    });
  }
}

export default SearchFilter;

src\App.css
.restcontainer {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-evenly;
}

.rest {
    box-shadow: 0 0 5px grey;
    border-radius: 5px;
    margin: 10px;
    width: 450px;

    font-family: touse;

    transition-property: box-shadow;
    transition-duration: 0.25s;
}

.rest:hover {
    box-shadow: 0 0 15px grey;
}

.sepline {
    height: 1px;
    width: 100%;
    background-color: rgb(230, 230, 230);
    margin-top: 5px;
}

.restinfo {
    padding: 10px;
    cursor: default;
}

.restcity {
    font-weight: bold;
    font-size: 12px;
}

.restname {
    font-size: 20px;
}

.restcuisines {
    font-size: 15px;
}

.restcuisine {
    margin: 2px;
    border: 1px solid grey;
}

.restfilter {
    display: flex;
    justify-content: space-evenly;
    align-items: center;
    font-family: touse;
    padding: 10px;
}

.reststats {
    display: flex;
    align-items: center;
    justify-content: space-evenly;

    padding: 10px;
    cursor: default;

    transition-property: background-color, color;
    transition-duration: 0.25s;
}

.reststats:hover {
    background-color: cornflowerblue;
    color: white;
}

src\data.json
[
  {
    "Name": "Martine of Martine's Table",
    "City": "Amsterdam",
    "Cuisine Style": "['French', 'Dutch', 'European']",
    "Ranking": 1,
    "Rating": 5,
    "Number of Reviews": 136
  },
  {
    "Name": "De Silveren Spiegel",
    "City": "Amsterdam",
    "Cuisine Style": "['Dutch', 'European', 'Vegetarian Friendly', 'Gluten Free Options']",
    "Ranking": 2,
    "Rating": 4.5,
    "Number of Reviews": 812
  },
  {
    "Name": "La Rive",
    "City": "Amsterdam",
    "Cuisine Style": "['Mediterranean', 'French', 'International', 'European', 'Vegetarian Friendly', 'Vegan Options']",
    "Ranking": 3,
    "Rating": 4.5,
    "Number of Reviews": 567
  },
  {
    "Name": "Vinkeles",
    "City": "Amsterdam",
    "Cuisine Style": "['French', 'European', 'International', 'Contemporary', 'Vegetarian Friendly', 'Vegan Options', 'Gluten Free Options']",
    "Ranking": 4,
    "Rating": 5,
    "Number of Reviews": 564
  },
  {
    "Name": "Librije's Zusje Amsterdam",
    "City": "Amsterdam",
    "Cuisine Style": "['Dutch', 'European', 'International', 'Vegetarian Friendly', 'Vegan Options', 'Gluten Free Options']",
    "Ranking": 5,
    "Rating": 4.5,
    "Number of Reviews": 316
  },
  {
    "Name": "Ciel Bleu Restaurant",
    "City": "Amsterdam",
    "Cuisine Style": "['Contemporary', 'International', 'Vegetarian Friendly', 'Vegan Options', 'Gluten Free Options']",
    "Ranking": 6,
    "Rating": 4.5,
    "Number of Reviews": 745
  },
  {
    "Name": "Zaza's",
    "City": "Amsterdam",
    "Cuisine Style": "['French', 'International', 'Mediterranean', 'European', 'Vegetarian Friendly', 'Vegan Options', 'Gluten Free Options']",
    "Ranking": 7,
    "Rating": 4.5,
    "Number of Reviews": 1455
  },
  {
    "Name": "Blue Pepper Restaurant And Candlelight Cruises",
    "City": "Amsterdam",
    "Cuisine Style": "['Asian', 'Indonesian', 'Vegetarian Friendly', 'Vegan Options', 'Gluten Free Options']",
    "Ranking": 8,
    "Rating": 4.5,
    "Number of Reviews": 675
  },
  {
    "Name": "Teppanyaki Restaurant Sazanka",
    "City": "Amsterdam",
    "Cuisine Style": "['Japanese', 'Asian', 'Vegetarian Friendly', 'Vegan Options', 'Gluten Free Options']",
    "Ranking": 9,
    "Rating": 4.5,
    "Number of Reviews": 923
  },
  {
    "Name": "Rob Wigboldus Vishandel",
    "City": "Amsterdam",
    "Cuisine Style": "['Dutch', 'Seafood', 'Fast Food']",
    "Ranking": 10,
    "Rating": 4.5,
    "Number of Reviews": 450
  },
  {
    "Name": "The Happy Bull",
    "City": "Amsterdam",
    "Cuisine Style": "['American', 'Bar', 'European', 'Vegetarian Friendly', 'Gluten Free Options']",
    "Ranking": 11,
    "Rating": 4.5,
    "Number of Reviews": 295
  },
  {
    "Name": "Gartine",
    "City": "Amsterdam",
    "Cuisine Style": "['French', 'Dutch', 'International', 'European', 'Vegetarian Friendly', 'Vegan Options', 'Gluten Free Options']",
    "Ranking": 12,
    "Rating": 4.5,
    "Number of Reviews": 967
  },
  {
    "Name": "Restaurant Adam",
    "City": "Amsterdam",
    "Cuisine Style": "['French', 'European', 'Central European', 'Vegetarian Friendly', 'Vegan Options', 'Gluten Free Options']",
    "Ranking": 13,
    "Rating": 4.5,
    "Number of Reviews": 368
  },
  {
    "Name": "Biercafe Gollem",
    "City": "Amsterdam",
    "Cuisine Style": "['Bar', 'Pub']",
    "Ranking": 14,
    "Rating": 4.5,
    "Number of Reviews": 586
  },
  {
    "Name": "Restaurant Daalder",
    "City": "Amsterdam",
    "Cuisine Style": "['French', 'Dutch', 'International', 'European', 'Vegetarian Friendly', 'Vegan Options', 'Gluten Free Options']",
    "Ranking": 15,
    "Rating": 4.5,
    "Number of Reviews": 1246
  },
  {
    "Name": "Greenwoods Keizersgracht",
    "City": "Amsterdam",
    "Cuisine Style": "['Dutch', 'Cafe', 'European', 'British', 'Vegetarian Friendly', 'Vegan Options', 'Gluten Free Options']",
    "Ranking": 16,
    "Rating": 4.5,
    "Number of Reviews": 1391
  },
  {
    "Name": "Omelegg - City Centre",
    "City": "Amsterdam",
    "Cuisine Style": "['Dutch', 'European', 'Healthy', 'International', 'Vegetarian Friendly', 'Gluten Free Options']",
    "Ranking": 17,
    "Rating": 4.5,
    "Number of Reviews": 1633
  },
  {
    "Name": "Brasserie Ambassade",
    "City": "Amsterdam",
    "Cuisine Style": "['French', 'Bar', 'International', 'European', 'Seafood', 'Vegetarian Friendly', 'Gluten Free Options']",
    "Ranking": 18,
    "Rating": 4.5,
    "Number of Reviews": 958
  },
  {
    "Name": "Sherpa Restaurant",
    "City": "Amsterdam",
    "Cuisine Style": "['Indian', 'Tibetan', 'Nepali', 'Vegetarian Friendly', 'Vegan Options', 'Gluten Free Options']",
    "Ranking": 19,
    "Rating": 4.5,
    "Number of Reviews": 426
  },
  {
    "Name": "La Maschera Lillotatini",
    "City": "Amsterdam",
    "Cuisine Style": "['Italian', 'Mediterranean', 'European', 'Vegetarian Friendly']",
    "Ranking": 20,
    "Rating": 4.5,
    "Number of Reviews": 421
  },
  {
    "Name": "Senses Restaurant",
    "City": "Amsterdam",
    "Cuisine Style": "['International', 'European']",
    "Ranking": 21,
    "Rating": 4.5,
    "Number of Reviews": 1380
  }  
]


Most Helpful This Week