After a few years of lurking without an account, I am today posting my first question.
I've been working on a personal project for a little while, to learn, trying to create a store-like website that has it's inventory of items read from json
files.
The functionality that I'm trying to achieve is something of a filter: type a tag
in a search field and only
the items that have that tag will be displayed
.
I've successfully managed to create a functional search filter using useState
& Array.filter()
with the help of online searches, but this is because I've used it on strings.
For instance, one of my json
files may have items
looking like this:
[
{
"title": "Name of the First Product",
"description": "Short Description",
"image": "/assets/img/image_001.png",
"price": "50.00",
"currency": "$",
.
.
.
"tags": [ "tag1", "tag2", "tag3"]
},
{
"title": "Name of the Second Product",
"description": "Short Description",
"image": "/assets/img/image_001.png",
"price": "55.00",
"currency": "$",
.
.
.
"tags": [ "tag2", "tag3", "tag4"]
},
.
.
.
]
The filter functionality is working for strings. If I'm typing First Product
in the search field, the correct item is rendered and nothing else is rendered.
However, this is not the case for arrays of tags. Nothing is rendered when I'm trying to search for a tag (array of strings).
As seen above, some of the tags may repeat, so I'm merging all the tags into one final string and eliminating the duplicates, before the filter function. For the sake of the example, let's say it looks like the following (note: .toLowerCase() is already applied to each one of the tags in the const below):
const finalArrayOfTags = [ "tag1", "tag2", "tag3", "tag4", ... ];
The way I'm filtering is:
function Function() {
const [query, setQuery] = useState("");
const fileData = require("/src/files/inventory/list.json");
() => {
JSON.parse(fileData);
}
const tagArray = fileData.map(i => i.tags);
const tagFlatten = tagArray.flat();
const tagToLower = tagFlatten.map(j => j.toLowerCase());
const finalArrayOfTags = [...new Set(tagToLower)];
return (
<Row>
<Form>
<Form.Row>
<Form.Control type="text" placeholder="Search" onChange={event => setQuery(event.target.value)}/>
</Form.Row>
</Form>
</Row>
<Row>
{fileData.filter(items => {
if (query === "") {
return item;
} else if (item.title.toLowerCase().includes(query.toLowerCase())) {
return item;
} else if (item.description.toLowerCase().includes(query.toLowerCase())) {
.
.
.
} else if (finalArrayOfTags.forEach(tag => tag.includes(query))) {
return item;
}}).map(item, index => (
<>
/// ... render the item component
</>
))}
</Row>
);
}
export default Function;
The first 2 "else if"s are rendering the correct items, because they are looking inside a string. The "else if" at the end doesn't render anything, because it's looking inside an array of strings and I can't figure out how to split it out or iterate through them.
Does anyone have any advice for me ? :)
CodePudding user response:
Array.forEach
does not return anything hence the check always fails. What you want to use instead is either .every
or .some
Both methods behave the same way like like other array methods, the main difference is,
In the case of .every
it will only return true if all the elements of that array satisfy the tags.include
Where as in the case of .some
it will return true as long as any one item satisfied the tags.include
So your final code might look like
else if (finalArrayOfTags.some(tag => tag.includes(query))) {
return item;
}}).map(item, index => (
<>
/// ... render the item component
</>
))}
CodePudding user response:
Check the following code
import React, {useState} from "react";
export default function App() {
const [searchKey, setSearchKey] = useState('');
// SAMPLE DATA
const [data, setData] = useState([{
name: 'Product 1',
tags: ['tag1', 'tag2', 'tag3']
},{
name: 'Product 2',
tags: ['tag2', 'tag3']
},{
name: 'Product 4',
tags: ['tag4', 'tag5',]
}])
return (
<div>
{/* SEARCH BAR */}
<input placeholder="Search..." value={searchKey} onChange={e => setSearchKey(e.target.value)}/>
<ul>
{
data.filter(e => {
// IF NO SEARCH KEYWORD FOUND - Returns all data
if(!searchKey.trim()){
return true;
}
// SEARCH ON TAGS AND PRODUCT NAME
return e.tags.indexOf(searchKey) !== -1?true:false || new RegExp(searchKey, 'gi').test(e.name);
}).map((e,k) => (
<li key={k}>{e.name}</li>
))
}
</ul>
</div>
);
}
CodePudding user response:
Use like this finalArrayOfTags.find( (tag, index ) => tag.includes(query) )
in the else if statement , I mean use it like
const finalArrayOfTags = ['tag1', 'tag2', 'tag3'];
let result;
if (result = finalArrayOfTags.find( (tag, index ) => tag.includes('tag1') )){
item = result;
return item;
}