when I search, I only want words starting with A or B....Z
Sample:
A Au B Be
Allow August Banner Bemol
August Bemol
Animal
searchFilter = (text) => {
const newData = dizi.filter((item) => {
const listItem = `${item.name.toLowerCase()}`;
const textData = text.toLowerCase();
return listItem.indexOf(textData) > -1;
});
this.setState({
diziler: newData,
});
};
There is a built-in string method .startsWith that you can use. It is case-sensitive, so you'll want to use .toLowerCase() on both the text and the item.name.
searchFilter = (text) => {
this.setState({
diziler: dizi.filter((item) =>
item.name.toLowerCase().startsWith(text.toLowerCase())
);,
});
};
searchFilter = (text) => {
const newData = dizi.filter((item) => {
if (
item.name.toLowerCase().startsWith("a") ||
item.name.toLowerCase().startsWith("b")
) {
const listItem = `${item.name.toLowerCase()}`;
const textData = text.toLowerCase();
return listItem.indexOf(textData) > -1;
}
});
this.setState({
diziler: newData,
});
};
Related
I have created two tabs that when clicked need to show a different set of products and a different set of filters for each selection. My problem is that when I click either tab and call setOptions within changeTab, I need to click each tab twice before it will update 'options', 'options' needs to contain each filter.
Obviously calling setOptions within the click handler is not correct but I can't figure out where or how to correctly update 'options'. Help greatly appreciated.
In the console logs below 'dedupedOptions' updates correctly on click
function filterProducts() {
const [categoryType, setCategory] = useState("Canine");
const [activeTabIndex, setActiveTabIndex] = useState(0);
const {
productData: {
products: { products }
}
} = useContext(AppContext);
const productsByCategory = products
.filter((product) => {
const { tags } = product;
return !!tags.find((tag) => tag.includes(categoryType));
})
.map((product) => ({
...product,
category: product.tags
.find((tag) => tag.includes("category:"))
.split(":")[1]
}));
let dedupedOptions = [];
productsByCategory.forEach((product) => {
const { tags } = product;
tags.forEach((tag) => {
const parts = tag.split(":");
const key = parts[0];
const value = parts[1] || null;
const validTag = tagKeysToDisplay.find(
(tagKeyToDisplay) => tagKeyToDisplay === key
);
if (
validTag &&
!dedupedOptions.find((dedupedOption) => dedupedOption.value === value)
) {
dedupedOptions = [
...dedupedOptions,
{
label: titleCase(value),
value,
selected: false
}
];
}
});
});
const [options, setOptions] = useState(dedupedOptions);
console.log(dedupedOptions);
console.log(options);
const changeTab = (index, category) => {
setCategory(category);
setActiveTabIndex(index);
setOptions(dedupedOptions);
};
const setFilter = useCallback(
(selectedOption) => {
const optionIsActive = options.find(
(option) => option.value === selectedOption.value
)?.selected;
let newOptions = [];
newOptions = [
...options.map((option) => {
if (option.value === selectedOption.value) {
return {
...option,
selected: !optionIsActive
};
}
return option;
})
];
setOptions(newOptions);
},
[options]
);
}
And the two elements set up as tabs to handle the click events. These are rendered within the same filterProducts function.
<div className="filter-products__tabs">
<div
className={`filter-products__tab
${activeTabIndex === 0 ? "is-active" : ""}`}
onClick={changeTab.bind(this, 0, "Canine")}
>
<span>DOG</span>
</div>
<div
className={`filter-products__tab
${activeTabIndex === 1 ? "is-active" : ""}`}
onClick={changeTab.bind(this, 1, "Feline")}
>
<span>CAT</span>
</div>
</div>
I reproduced your question by some changes in variable declarations in state.
be careful to declare variables in state and do the updates by listening the variable changes inside the useEffect.
here is the working code:\
https://codesandbox.io/s/quirky-http-e264i?file=/src/App.js
import "./styles.css";
import { useState, useContext, useCallback, useEffect } from "react";
export default function App() {
const [categoryType, setCategory] = useState("Canine");
const [activeTabIndex, setActiveTabIndex] = useState(0);
const [productsByCategory, setProductsByCategory] = useState([]);
const [dedupedOptions, setDedupedOptions] = useState([]);
const [options, setOptions] = useState(dedupedOptions);
const products = [
{ tags: ["category:Feline"], name: "one" },
{ tags: ["category:Canine"], name: "two" }
];
useEffect(() => {
const productsByCategory = products
.filter((product) => {
const { tags } = product;
return !!tags.find((tag) => tag.includes(categoryType));
})
.map((product) => ({
...product,
category: product.tags
.find((tag) => tag.includes("category:"))
.split(":")[1]
}));
setProductsByCategory(productsByCategory);
}, [categoryType]);
useEffect(() => {
let tmp_dedupedOptions = [];
const tagKeysToDisplay = ["category"];
productsByCategory.forEach((product) => {
const { tags } = product;
tags.forEach((tag) => {
const parts = tag.split(":");
const key = parts[0];
const value = parts[1] || null;
const validTag = tagKeysToDisplay.find(
(tagKeyToDisplay) => tagKeyToDisplay === key
);
if (
validTag &&
!tmp_dedupedOptions.find(
(dedupedOption) => dedupedOption.value === value
)
) {
tmp_dedupedOptions = [
...tmp_dedupedOptions,
{
label: value,
value,
selected: false
}
];
}
});
});
setDedupedOptions(tmp_dedupedOptions);
setOptions(tmp_dedupedOptions);
}, [productsByCategory]);
console.log("options: ", options);
const changeTab = (index, category) => {
setCategory(category);
setActiveTabIndex(index);
};
const setFilter = useCallback(
(selectedOption) => {
const optionIsActive = options.find(
(option) => option.value === selectedOption.value
)?.selected;
let newOptions = [];
newOptions = [
...options.map((option) => {
if (option.value === selectedOption.value) {
return {
...option,
selected: !optionIsActive
};
}
return option;
})
];
setOptions(newOptions);
},
[options]
);
// }
return (
<div>
<div className="filter-products__tabs">
<div
className={`filter-products__tab
${activeTabIndex === 0 ? "is-active" : ""}`}
onClick={changeTab.bind(this, 0, "Canine")}
>
<span>DOG</span>
</div>
<div
className={`filter-products__tab
${activeTabIndex === 1 ? "is-active" : ""}`}
onClick={changeTab.bind(this, 1, "Feline")}
>
<span>CAT</span>
</div>
</div>
</div>
);
}
I am trying to use the tagPicker from fluent ui. I am using as starting point the sample from the site:
https://developer.microsoft.com/en-us/fluentui#/controls/web/pickers
The problem is that the object I have has 3 props. the objects in the array are {Code:'string', Title:'string', Category:'string'}. I am using a state with a useeffect to get the data. SO far works fine, the problem is that the suggestion are rendered blank. It filter the items but does not show the prop I want.
Here is my code:
import * as React from 'react';
import {
TagPicker,
IBasePicker,
ITag,
IInputProps,
IBasePickerSuggestionsProps,
} from 'office-ui-fabric-react/lib/Pickers';
import { mergeStyles } from 'office-ui-fabric-react/lib/Styling';
const inputProps: IInputProps = {
onBlur: (ev: React.FocusEvent<HTMLInputElement>) => console.log('onBlur called'),
onFocus: (ev: React.FocusEvent<HTMLInputElement>) => console.log('onFocus called'),
'aria-label': 'Tag picker',
};
const pickerSuggestionsProps: IBasePickerSuggestionsProps = {
suggestionsHeaderText: 'Suggested tags',
noResultsFoundText: 'No color tags found',
};
const url="url_data"
export const TestPicker: React.FunctionComponent = () => {
const getTextFromItem = (item) => item.Code;
const [state, setStateObj] = React.useState({items:[],isLoading:true})
// All pickers extend from BasePicker specifying the item type.
React.useEffect(()=>{
if (!state.isLoading) {
return
} else {
caches.open('cache')
.then(async cache=> {
return cache.match(url);
})
.then(async data=>{
return await data.text()
})
.then(data=>{
const state = JSON.parse(data).data
setStateObj({items:state,isLoading:false})
})
}
},[state.isLoading])
const filterSuggestedTags = (filterText: string, tagList: ITag[]): ITag[] => {
return filterText
? state.items.filter(
tag => tag.Code.toLowerCase().indexOf(filterText.toLowerCase()) === 0 && !listContainsTagList(tag, tagList),
).slice(0,11) : [];
};
const listContainsTagList = (tag, state?) => {
if (!state.items || !state.items.length || state.items.length === 0) {
return false;
}
return state.items.some(compareTag => compareTag.key === tag.key);
};
return (
<div>
Filter items in suggestions: This picker will filter added items from the search suggestions.
<TagPicker
removeButtonAriaLabel="Remove"
onResolveSuggestions={filterSuggestedTags}
getTextFromItem={getTextFromItem}
pickerSuggestionsProps={pickerSuggestionsProps}
itemLimit={1}
inputProps={inputProps}
/>
</div>
);
};
I just got it, I need to map the items to follow the {key, name} from the sample. Now it works.
setStateObj({items:state.map(item => ({ key: item, name: item.Code })),isLoading:false})
I want to push an item to the list if its not previously included there. If its there then remove that item. I am able to do the first part, but no idea about how to remove that.
handleCityCheckbox = (param1) => {
var { cityList = [] } = this.state;
//if cityList doesnt have param1
if (!cityList.includes(param1)) {
cityList.push(param1);
this.setState({ cityList });
} else {
}
this.setState({ cityList });
};
what will be the else part?
handleCityCheckbox = (param1) => {
const { cityList = [] } = this.state;
const itemIndex = cityList.indexOf(param1);
if (itemIndex === -1)) {
cityList.push(param1);
} else {
cityList = cityList.filter((e, index) => index !== itemIndex)
}
this.setState({ cityList });
};
Finished App:
Filtering function:
const handleSubmit = (event) => {
event.preventDefault();
if (!name) {
alert("Enter the city name");
return;
}
let tempList = cities.filter(
(city) => city.toLowerCase() !== name.toLowerCase()
);
if (tempList.length === cities.length) {
tempList.push(name);
setCities(tempList);
return;
} else {
setCities(tempList);
}
};
In the above function, we will, first of all, use filter function to filter out i.e. delete the city name if it exists and assign it to tempList, then we compare the size of tempList with main cities list, if it's same then it indicates that the city name was not present in the main list so we will push that name to tempList and update the cities state with modified tempList, else, we just set the filtered out tempList.
Full Example :
import React, { useState } from "react";
import "./styles.css";
export default function App() {
const [cities, setCities] = useState(["Pune", "Delhi"]);
const [name, setName] = useState("");
const handleSubmit = (event) => {
event.preventDefault();
if (!name) {
alert("Enter the city name");
return;
}
let tempList = cities.filter(
(city) => city.toLowerCase() !== name.toLowerCase()
);
if (tempList.length === cities.length) {
tempList.push(name);
setCities(tempList);
return;
} else {
setCities(tempList);
}
};
return (
<div className="App">
<form onSubmit={handleSubmit}>
<input onChange={(event) => setName(event.target.value)} />
<button type="submit">Submit</button>
</form>
{cities.map((city) => (
<p>{city}</p>
))}
</div>
);
}
Codesandbox Link
I have a list of users with name, gender, age, etc from the following api: [https://gorest.co.in/public-api/users]. At the moment I can filter by gender === female, but doesn't work when I filter for gender === male.
Also, I try to filter the list of users by age. I get the date of birth and sorted afterwards, but it doesn't seem enough.
I have a LIVE EXAMPLE HERE
Here is the code:
import React from "react";
import axios from "axios";
export default class UserList extends React.Component {
constructor(props) {
super(props);
this.state = {
list: [],
search: ""
};
}
componentDidMount() {
this.getList();
}
/* get users list */
getList = async () => {
const api = 'https://gorest.co.in/public-api/users?_format=json&access-token=3qIi1MDfD-GXqOSwEHHLH73Y3UitdaFKyVm_';
await axios
.get(api)
.then(response => {
const list = response.data.result;
this.setState({
list,
isLoading: false
});
})
.catch(err => {
console.log(err);
});
};
/* handler for search bar */
handleChange = e => {
this.setState({
search: e.target.value
});
};
filterGender = gender => {
const lowerCaseGender = gender.toLowerCase();
const filteredGender = this.state.list.filter(
user => user.gender.toLowerCase().indexOf(lowerCaseGender) !== -1
);
this.setState({ list: filteredGender }, () => console.log(this.state.list));
};
filterAge = () => {
const ageList = this.state.list.map(age => {
return age.dob;
});
const filteredAge = this.state.list.filter(
e => e.dob.indexOf(ageList) !== -1
);
this.setState({ list: filteredAge }, () => console.log(this.state.list));
};
render() {
let style = {
display: "grid",
gridTemplateColumns: "repeat(auto-fill, minmax(250px, 1fr))",
padding: "1rem",
gridGap: "1rem 1rem"
};
return (
<div>
<input
placeholder="Search for a user..."
onChange={e => this.handleChange(e)}
/>
<button onClick={() => this.filterGender("male")}>Male</button>
<button onClick={() => this.filterGender("female")}>Female</button>
<button onClick={() => this.filterAge()}>Age</button>
<ul style={style}>
{this.state.list.map(user => {
return (
<li key={user.id}>
<div>
<img className="thumb" alt="" src={user._links.avatar.href} />
</div>
<div className="userInfo">
<p>
{user.first_name} {user.last_name}
</p>
</div>
</li>
);
})}
</ul>
</div>
);
}
}
Thank you!
The condition you are using,
user.gender.toLowerCase().indexOf(lowerCaseGender) !== -1
is matching male in female due to indexOf.
You should do this,
const filteredGender = this.state.list.filter(
user => user.gender.toLowerCase() === lowerCaseGender
);
There are two problems in your code.
Here I have updated your code.
https://codesandbox.io/s/epic-swartz-iu3qp
Problem 1: use of indexOf in filterGender function
Solution 1: directly compare the gender instead of indexOf
const filteredGender = this.list.filter(
user => user.gender.toLowerCase() === lowerCaseGender
);
Problem 2: You are filtering records from the this.state.list variable and then update it in same state variable.
Solution 2: Use another variable this.list in constructor and use it to filter the records.
Define variable in constructor
constructor(props) {
super(props);
this.state = {
list: [],
search: ""
};
this.list = []; // define new list here...
}
You have to assign upcoming list to this.list variable in getList function
this.list = response.data.result;
getList function should be look like below.
getList = async () => {
const api =
"https://gorest.co.in/public-api/users?_format=json&access-token=3qIi1MDfD-GXqOSwEHHLH73Y3UitdaFKyVm_";
await axios
.get(api)
.then(response => {
this.list = response.data.result; // assign list in variable
this.setState({
list:this.list,
isLoading: false
});
})
.catch(err => {
console.log(err);
});
};
Now you have to update both filter function as below. Instead of this.state.list use this.list.
filterGender = gender => {
const lowerCaseGender = gender.toLowerCase();
const filteredGender = this.list.filter(
user => user.gender.toLowerCase() === lowerCaseGender
);
this.setState({ list: filteredGender }, () => console.log(this.state.list));
};
filterAge = () => {
const ageList = this.list.map(age => {
return age.dob;
});
const filteredAge = this.list.filter(
e => e.dob.indexOf(ageList) !== -1
);
this.setState({ list: filteredAge }, () => console.log(this.state.list));
};
Hope this will work for you!
Full working demo with corrected code in same environment
https://codesandbox.io/s/brave-carson-6zilx
Gender
Issue - indexOf function use does't achieves anything.
Resolution -:
Using simple Array.filter with gender equality.
This function needs to be changed for gender filter -:
filterGender = gender => {
const filteredGender = this.state.list.filter(
user => user.gender.toLowerCase() === gender
);
this.setState({ list: filteredGender }, () => console.log(this.state.list));
};
Age
Issue - Filtering won't be helpful to sort data.
Resolution -:
Using Array.sort and adding a comparator function.
This function needs to be changed for age sorting -:
filterAge = () => {
const filteredAge = this.state.list.sort(function(a, b) {
return new Date(b.dob) - new Date(a.dob);
});
this.setState({ list: filteredAge }, () => console.log(this.state.list));
};
I am have trouble figuring out how to get this code to work. I am trying to remove a todo item based on which one was clicked. My goal eventually is to delete based on which button was clicked.
Here is my code so far:
import uuidv4 from 'uuid/v4'
let text;
let todos = [];
document.querySelector('#new-todo').addEventListener('keypress', e =>
{
text = e.target.value;
if (e.keyCode === 13 || e.which === 13) {
e.preventDefault();
addTodo(text);
renderTodo();
console.log(todos);
}
});
const addTodo = text =>
{
todos.push(
{
id: uuidv4(),
text
});
}
const renderTodo = () =>
{
let node = document.createElement("p");
let textnode = document.createTextNode(text);
node.appendChild(textnode);
document.getElementById("todos").appendChild(node);
}
document.querySelector('#todos').addEventListener('click', () =>
{
removeTodo(todo.id);
console.log(todos);
});
const removeTodo = id =>
{
const todoIndex = todos.findIndex((todo) => todo.id === id)
if (todoIndex > -1)
{
todos.splice(todoIndex, 1);
}
};