While selecting one option in the Select box at that time rest of the options are become multiple values. How can i prevent this duplicate values ?
import Select from 'react-select';
const dataOptions = [];
class App extends React.Component {
constructor(props) {
super(props);
this.data = [];
this.getData();
}
getData = () => { api.request({url: `/getdata`}).then(res => res.map(el => this.data[el.id] = el.name)) }
addData = () => {
const { selectedId } = this.state;
var datas = this.data;
console.log(datas);
datas.map((name, index) => {
if (!dataOptions.includes(name)) {
console.log('b4 push:', dataOptions)
dataOptions.push({ value: index, label: name });
console.log('aftr push:', dataOptions)
}
});
return (
<Select options={dataOptions}
isMulti
/>
);
}
}
Something is wrong happening in this syntax:
datas.map((name, index) => {
if (!dataOptions.includes(name)) {
dataOptions.push({ value: index, label: name });
}
});
Console Results
[ "data-1", "data-2", "data-3"]
b4 push: [
{value: 1, label: "data-1"}
{value: 2, label: "data-2"}
{value: 3, label: "data-3"}
]
aftr push: [
{value: 1, label: "data-1"}
{value: 2, label: "data-2"}
{value: 3, label: "data-3"}
]
P.S: Here in aftr push i have already selected first option from drop down; so in result if should not be displayed in the array values.
Thanks in advance...!
The destructuring syntax should be like below
datas.map(({name, index}) => {
if (!dataOptions.includes(name)) {
dataOptions.push({ value: index, label: name });
}
});
Moreover you don't need external array to push the data inside map function as the function by default returns an array, you can do simply like below
let expected_data=datas.map(({name, index}) => {
if (!dataOptions.includes(name)) {
return { value: index, label: name };// return a value
}
});
The expected_data will contain the data you need after operation
See the snippet-
let data = [{
"name": 1,
"index": 2
}, {
"name": 11,
"index": 21
}]
console.log(data.map(({
index,
name
}) => {
return {
value: index,
label: name
}
}))
You better use Array.some() for what you are looking
datas.map((name,index) => { // here index is the iterator
if(!dataOptions.some(({value,label})=>label==name ))
{
dataOptions.push({ value: index, label: name });
}
});
Related
My brain froze with this advanced filtering. This task has exceeded my basic knowledge of filter, map etc.
Here I have an array with nested objects with array:
const DATA = [
{
title: 'Spongebob',
data: [
{ id: 1, name: 'Mr Crabs' },
{ id: 2, name: 'Sandy' }
]
},
{
title: 'Dragon Balls Z',
data: [
{ id: 1, name: 'GoKu' },
{ id: 2, name: 'Zamasu' }
]
}
];
You may have seen this sort of style if you've worked with React Native (RN). This question is not for RN. I need to perform a filter on the name property in the nested array and when I get a match, I must return the format as the DATA variable.
const handleFiltering = (value) => {
const _value = value.toLowerCase();
const results = DATA.map(o => {
return o.data.filter(o => o.name.toLowerCase().indexOf(_value) != -1)
});
console.log(results);
};
My limited knowledge of deep filtering returns the basic filtering for the data array but need to retain the structure for DATA. The expected results I'd expect:
// I'm now querying for "ZAMASU"
const handleFiltering = (value='ZAMA') => {
const _value = value.toLowerCase();
const results = DATA.map(o => {
return o.data.filter(o => o.name.toLowerCase().indexOf(_value) != -1)
});
// console.log(results) should now be
// [
// {
// title: 'Dragon Balls Z',
// data: [
// { id: 2, name: 'Zamasu' }
// ]
// }
// ];
};
What comes to mind is the use of {...DATA, something-here } but my brain has frozen as I need to get back the title property. How to achieve this, please?
Another solution would be first use filter to find only objects containing the name in data passed through the argument, subsequently mapping data.
Here is your adjusted filter method
const handleFiltering = (value) => {
const _value = value.toLowerCase();
const results = DATA.filter((obj) =>
obj.data.some((character) => character.name.toLowerCase() === _value)
).map((obj) => ({
title: obj.title,
data: obj.data.filter(
(character) => character.name.toLowerCase() === _value
),
}));
console.log(results);
};
You can use reduce method of array. First find out the object inside data array and then add that to accumulator array as new entry by preserving the original structure.
const DATA = [
{
title: 'Spongebob',
data: [
{ id: 1, name: 'Mr Crabs', where: 'tv' },
{ id: 2, name: 'Sandy' }
]
},
{
title: 'Dragon Balls Z',
data: [
{ id: 1, name: 'GoKu' },
{ id: 2, name: 'Zamasu' }
]
}
];
let handleFiltering = (value='tv') => {
return DATA.reduce((acc,d) => {
let obj = d.data.find(a => a.name?.toLowerCase().includes(value.toLowerCase())
|| a.where?.toLowerCase().includes(value.toLowerCase()));
obj ? acc.push({...d, data:[obj]}) : null;
return acc;
}, []);
}
let result = handleFiltering();
console.log(result);
I have a state and within that state there is a list with several objects. What I basically want to do is take an index from this array, in this case [0], and change its state. So, in short, I want to take {id: "1", value: "world1"}, and change the id to 'something'. I made a code but it didn't come out as I expected.
this.state = {
myState: [
{ id: "1", value:"world1" },
{ id: "2", value:"world2" },
{ id: "3", value:"world3" },
]
}
const result = this.setState(prevState => ({
myState: {
...prevState[0].id,
id: 'something'
}
}))
console.log(result)
Not sure why you need this, but the issue I'm seeing here is that myState is initially an array, and then you're passing it back as an object.
Try this:
const result = this.setState(prevState => {
prevState.myState[0].id = 'something';
return prevState;
})
const state = {
myState: [{
id: "1",
value: "world1"
},
{
id: "2",
value: "world2"
},
{
id: "3",
value: "world3"
},
]
}
const setState = (({
myState
}) => {
const newState = {
myState: [...myState]
}
const array = newState.myState
const targetIndex = array.findIndex(({
id
}) => id === '1')
const item = array[targetIndex]
array[targetIndex] = { ...item,
id: 'something'
}
return newState;
})
console.log(setState(state))
Use Array.prototype.map and object spread operator
this.setState(prevState => {
return {
myState: prevState.myState.map(
myStateItem => {
if(myStateItem.id === '1') {
return {
...myStateItem,
id: 'something'
}
}
return {
...myStateItem
}
})
};
});
Hi I have object like this:
Subject {
id:1
packages: [
{id:1, isChecked:true, ...},
{id:2, isChecked:false, ...},
{id:3, isChecked:true, themes:[
{id:1, isChecked:true},
{id:1, isChecked:false},
]
]
...
}
How can i remove all not checked items from this object please? I have to send this updated object to another component in react app.
The real tree looks like this:
Subject
|-packages
|-themes (check if checked)
|-themeParts (check if checked)
|-exercises (check if checked)
If any of child is checked it should be added to new component. So if I have Two packages and only one exercise is checked it is also checked themmeparts of this exercise, also theme of theme part and theme. Packages don't have isChecked attribute but i have to add this level to new object too if any of its child is checked.
Other example... if second package has no theme,part or exercise checked i have to remove from package level down alll...
So when i finish i need to have only Subject{} object with checked items + package of that checked items...
I hope i described it good XD....
anyway i tried something like this:
returnSelectedItems(){
console.log(this.state.data);
let newData = cloneDeep(this.state.data);
newData.packages = [];
this.state.data.packages.forEach((pckg) => {
const newPackage = {
};
pckg.themes.forEach((theme, key) => {
if(theme.isChecked){
}
});
});
console.log(newData);
console.log(newData.packages);
console.log(newData.packages[0].themes);
console.log(newData.packages[0].themes[0].themeParts);
}
But this is useless i think and i really don't know how to od it properly and ezy as it can be.. Thx for help
You can create a generic function like this. This takes an array as input and reduces it recursively for any nested array property. Destructure the object and get the array property to a rest object. If the rest object has any keys, recursively call the function to filter for the isChecked property.
This will work for any name of the array property for any number of nesting
function getChecked(array) {
return array.reduce((acc, { id, isChecked, ...rest }) => {
if (isChecked) {
const o = { id, isChecked };
const [key] = Object.keys(rest);
if (key)
o[key] = getChecked(rest[key]);
acc.push(o)
}
return acc;
}, [])
}
const output = {
id: input.id,
packages: getChecked(input.packages)
};
Here's a snippet:
function getChecked(array) {
return array.reduce((acc, { id, isChecked, ...rest }) => {
if (isChecked) {
const o = { id, isChecked };
const [key] = Object.keys(rest);
if (key)
o[key] = getChecked(rest[key]);
acc.push(o)
}
return acc;
}, [])
}
const input = {
id: 1,
packages: [
{ id: 1, isChecked: true },
{ id: 2, isChecked: false },
{ id: 3, isChecked: true, themes: [
{
id: 4, isChecked: true, themeParts: [
{ id: 5, isChecked: true, exercises: [
{ id: 7, isChecked: true },
{ id: 8, isChecked: false }
]
},
{ id: 6, isChecked: true }
]
},
{ id: 9, isChecked: false }
]
},
{ id: 10, isChecked: true },
]
};
const output = {
id: input.id,
packages: getChecked(input.packages)
};
console.log(output)
I believe this is what you want. That's parent cannot be removed if children(themes) need to be kept
var subjects = {
id: 1,
packages: [{
id: 1,
isChecked: true,
},
{
id: 2,
isChecked: false,
},
{
id: 3,
isChecked: true,
themes: [{
id: 1,
isChecked: true
},
{
id: 1,
isChecked: false
},
]
}
]
};
function purge(item) {
purgeItems(item.themes ||[]);
return !item.isChecked && (item.themes ||[]).length === 0;
}
function purgeItems(themes) {
var toremove = [];
themes.forEach(function(p, i) {
if (purge(p)) {
toremove.push(i)
}
});
while (toremove.length > 0) {
themes.splice(toremove.pop(), 1);
}
}
purgeItems(subjects.packages);
console.log(JSON.stringify(subjects));
This is what i needed. using this principe: let {packages: packages, ...newData} = tempData;
returnSelectedItems(){
let tempData = cloneDeep(this.state.data);
let {packages: packages, ...newData} = tempData;
this.state.data.packages.forEach(pckg => {
const {themes,...newPckg} = pckg;
newPckg.themes = [];
pckg.themes.forEach(theme =>{
if(!theme.isChecked){
return;
}
const {themeParts, ...newTheme} = theme;
newTheme.themeParts =[];
newPckg.themes.push(newTheme);
theme.themeParts.forEach(part =>{
if(!part.isChecked){
return;
}
const {knowledges, ...newPart} = part;
newPart.knowledges = [];
newTheme.themeParts.push(newPart);
part.knowledges.forEach(knowledge =>{
if(!knowledge.isChecked){
return;
}
newPart.knowledges.push(knowledge);
});
});
});
if(newPckg.themes.length > 0){
newData.packages = newPckg;
}
});
return newData;
}
I have a few questions in regards to what would be the best approach to do the following:
Call two different API:
axios.get(contents);
axios.get(favorites);
Response will Look like this:
contents: [
{
id: 1,
value: someValue
},
{
id: 2,
value: someValue
}
];
favorites: [
{
id: 1,
contentId: 2
}
];
What would be the best approach to loop through each favorite and add an element to the contens array such as isFavorite: true when the contentId matches the id. It should look as follows:
contents: [
{
id: 1,
value: someValue
{,
{
id: 2,
value: someValue
isFavorite: true
{
];
What would be the best place to do this and is there any ES6 syntax that can easily do this? I currently have the two actions separate, one that gets the contents and one that gets the favorites, I could possibly merge those or combine them at the reducer.
Any suggestions?
You can use a Set to collect all contentId values from favorites and then iterate through your contents array. This has better time complexity than using some on an array because calling .has() on a Set is O(1):
let contents = [{
id: 1,
value: 'someValue1'
},
{
id: 2,
value: 'someValue2'
},
{
id: 3,
value: 'someValue'
}
];
let favorites = [{
id: 1,
contentId: 2
},
{
id: 2,
contentId: 3
}
];
let favoriteContents = new Set(favorites.map(f => f.contentId));
contents.forEach(c => {
if (favoriteContents.has(c.id)) c.isFavorite = true;
});
console.log(contents);
const newContents = contents.map((content) => {
const foundFavorite = favorites.find((favorite) => favorite.contentId === content.id)
if (foundFavorite) {
return {
...content,
isFavorite: true,
}
}
return content
});
You firstly need to have the promises from your API calls, and when both of them are complete you can then carry out the merge of the results.
const contentsApi = () => Promise.resolve([
{
id: 1,
value: 'foo'
},
{
id: 2,
value: 'bar'
}
])
const favouritesApi = () => Promise.resolve([
{
id: 1,
contentId: 2
}
])
let contents;
let favourites;
const contentsApiCall = contentsApi().then(res => {
contents = res;
})
const favouritesApiCall = favouritesApi().then(res => {
favourites = res;
})
Promise.all([contentsApiCall, favouritesApiCall]).then(() => {
const merged = contents.map(content => {
if(favourites.some(favourite => favourite.contentId === content.id)){
return {
...content,
isFavourite: true
}
} else {
return content;
}
})
console.log(merged)
// do whatever you need to do with your result, either return it if you want to chain promises, or set it in a variable, etc.
})
I'm trying to build onChange search feature. In suggestion list, my state like this:
results: {
page: 1,
results: [
{value: 'popularity.desc', label: 'Popularity Descending'},
{value: 'popularity.asc', label: 'Popularity Ascending'},
{value: 'new value', label: 'new label'},
{value: 'vote_average.desc', label: 'Rating Descending'}
]
}
When the user picks {value: 'new value', label: 'new label'} object.
How can I shift object to the first index of results array of state?
For example: After Object was picked. The state should be like this:
results: {
page: 1,
results: [
{value: 'new value', label: 'new label'},
{value: 'popularity.desc', label: 'Popularity Descending'},
{value: 'popularity.asc', label: 'Popularity Ascending'},
{value: 'vote_average.desc', label: 'Rating Descending'}
]
}
My idea is using spread operator and filter but I don't know how to implement that.
Select Item method:
onSuggestionSelected = (event, {suggestion, suggestionValue }) => {
console.log('Selected', suggestion); // {value: 'new value', label: 'new label'}
if (suggestion.media_type === 'movie') {
this.navigate('/search/movie', suggestionValue, suggestion);
} else if (suggestion.media_type === 'tv') {
this.navigate('/search/tv', suggestionValue, suggestion);
} else {
this.navigate('/search', suggestionValue, suggestion);
}
};
After selected, it will navigate:
navigate = (pathname, queryValue, resultValue) => {
// ResultValue is that Object that I want to shift first.
this.props.history.push({
pathname,
search: `?query=${queryValue}`,
state: {results: this.state.data}});
};
//filtered the remaining item
let remainValue = result.filter((obj, key) => obj.value != suggestion.value);
//merge here
let newResult = [suggestion, ...remainValue]; //it will contain reordered item
At first we need to find an index using findIndex, then filter current state to exclude the object containing selected data.
Last thing we need to do is to combine results into one array using spread operator ....
Example:
const currentState = [
{value: 'popularity.desc', label: 'Popularity Descending'},
{value: 'popularity.asc', label: 'Popularity Ascending'},
{value: 'new value', label: 'new label'},
{value: 'vote_average.desc', label: 'Rating Descending'}
];
const selectedValue = {value: 'new value', label: 'new label'};
const onSelectionChange = (selected) => {
const selectedIndex = currentState.findIndex(({ value }) => value === selected.value);
const selectedItem = currentState[selectedIndex];
const stateExcludedItem = currentState.filter(({ value }) => value !== selected.value);
const newState = [ selectedItem, ...stateExcludedItem ]
// this.setState
return newState;
}
const result = onSelectionChange(selectedValue);
console.log(result);
You could find the index of the result you want to put first in the array, and then put that first followed by what used to be before and after the result in the array.
Example
class App extends React.Component {
state = {
results: {
page: 1,
results: [
{ value: "popularity.desc", label: "Popularity Descending" },
{ value: "popularity.asc", label: "Popularity Ascending" },
{ value: "new value", label: "new label" },
{ value: "vote_average.desc", label: "Rating Descending" }
]
}
};
putResultFirst = result => {
this.setState(previousState => {
const { results } = previousState.results;
const resultIndex = results.indexOf(result);
return {
results: {
...results,
results: [
result,
...results.slice(0, resultIndex),
...results.slice(resultIndex + 1)
]
}
};
});
};
render() {
return (
<div>
{this.state.results.results.map(result => (
<div key={result.id} onClick={() => this.putResultFirst(result)}>
{result.value} {result.label}
</div>
))}
</div>
);
}
}
To move the selected item to the first position, you can use the Array.filter method to get the unselected items as an array, and build a new results array:
selectedItem = this.state.results.results[clickedIndex]
unselectedItems = this.state.results.results.filter((item, index) => clickedIndex !== index)
Because the "results" in state is an object. To update an object in the state, you should make an copy of the current results object to create a new object, so that the state will be updated.
results = {...this.state.results, results: [selectedItem, ...unselectedItems]}
You just want to move the selected item to the top of the results array, therefore you need just transfer the index of selected item to the event handler, which will call the setState method.
The example codes:
class App extends React.Component {
state = {
results: {
page: 1,
results: [
{value: 'popularity.desc', label: 'Popularity Descending'},
{value: 'popularity.asc', label: 'Popularity Ascending'},
{value: 'new value', label: 'new label'},
{value: 'vote_average.desc', label: 'Rating Descending'}
]
}
};
handelClick = (clickedIndex) => {
if(clickedIndex === 0) {
return;
}
let selectedItem = this.state.results.results[clickedIndex],
unselectedItems = this.state.results.results.filter((item, index) => clickedIndex !== index),
results = {...this.state.results, results: [selectedItem, ...unselectedItems]};
this.setState({results})
};
render() {
return <ul>
{this.state.results.results.map((item, index) =>
<li key={item.label} onClick={this.handelClick.bind('', index)}>{item.label}</li>)
}
</ul>
}
}
const renderFilterList = () => {
ReactDOM.render(<App />, document.getElementById('react-app'));
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="react-app"></div>