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 :
<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 :
<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>
<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>
{rest["Number of Reviews"]}
</div>
<div>
<i style={{ fontSize: "15px" }} className="far fa-star"></i>
{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
}
]