target array by passing argument in function in js/react - javascript

I have an object to collect data to send to an API:
apiData: {
colors: [],
years: [],
// ..
}
Many of the children of this apiData are arrays like colors and years, I call these 'subgroups'. A user can select a multitude of subgroups with checkboxes that trigger:
handleCheckboxColorChange(value, isChecked) {
let newApiData = '';
this.setState( (prevState) => {
if (isChecked === true) {
newApiData = {...prevState.apiData, colors: [...prevState.apiData.colors, value]}
} else {
newApiData = {...prevState.apiData, colors: [...prevState.apiData.colors.filter(item => item !== value)]
}
}
return {apiData: newApiData}
}, () => this.props.handleApiCall(this.state.apiData))
}
I use a similar function for the other 'subgroups'. For years, all that changes in the function is colors to years. So I wish to create a more general function that can take a 'subgroup' as argument to target the right array in my object. I tried to pass a third variable (a string) subGroup like so:
handleCheckboxChange(value, isChecked, subGroup) {
// ..
newApiData = {...prevState.apiData, subGroup: [...prevState.apiData.subGroup, value]}
This does not work (I guess because it is now looking for the child 'subgroup' in my object). How can I make this work?

Use bracket notation :
handleCheckboxChange(value, isChecked, subGroup) {
// ..
newApiData = {...prevState.apiData, [subGroup]: [...prevState.apiData[subGroup], value]}

To make it a bit prettier, you can use this:
handleCheckboxColorChange(value, isChecked, subGroup) {
this.setState((prevState) => {
const newState = { ...prevState }
newState[subGroup] = isChecked ? [ ...newState[subGroup], value ] : newState[subGroup].filter(item => item !== value)
return newState
}, () => this.props.handleApiCall(this.state.apiData))
}

Related

Verify if the value of input already exists in array

i have a issue, i have two inputs then and i can't permit the user save this edit if the two values is equals.
my state who contains a data from db
const [doctorRegisters, setDoctorResgisters] = useState<any>([]);
inside this array i contain this
[{"__typename": "DoctorMedicalRegister", "counsil": "CRM", "id": "141", "isMainRegister": true, "register": "1234567/RS"}, {"__typename": "DoctorMedicalRegister", "counsil": "CRM", "id": "153", "isMainRegister": false, "register": "1234567/RS"}]
and i need compare two register who if is equal, i cant permit user save he save just is different
here is a code who i try fix this
const isEquals = () => {
doctorRegisters.map((item: any) => {
if (item.register) {
doctorRegisters.map((item2: any) => {
if (item2.register) {
if (item.register === item2.register) {
console.log('iguais')
}
}
});
}
});
};
but this work only for dont edited values i need verify when this value is changed in input in this case i only verify in db this values is equals
here is my handle change
const handleEditRegisterCrm = (crm: string, id: number) => {
setDoctorResgisters(
doctorRegisters.map((item: any) => {
if (item && Number(item.id) == id) {
item.register = `${crm}/${item.register?.split('/')[1] || ''}`;
}
return item;
}),
);
};
You could do something like:
const handleEditRegisterCrm = (crm: string, id: number) => {
if (!doctorRegisters.some((doctorRegister) => doctorRegister.register.includes(registerToCompare)) {
setDoctorRegisters(
doctorRegisters.map((item: any) => {
if (item && Number(item.id) == id) {
item.register = `${crm}/${item.register?.split('/')[1] || ''}`;
}
return item;
}),
);
}
};
Remember you should keep track of the registerToCompare in order to find if it's already inserted in the doctorRegisters list. I'm assuming you can obtain that value from the your handleChange function.

How to fiter array with an array

I have a list of objects like this.
results = [
{ id: 1,
status : "Active"
// Some other fields
tags : [{val: 'IGM', color: 'light-success' },
{val: 'Gated Out', color: 'light-primary' },
]
},
// ...
]
now I want to filter objects in relation to tags,
the input to filter the list is also in the form of an array using multi-select input.
like
[{value: 'Gated Out', label: 'GATED OUT'}, .. ]
I'm able to filter data of other fields but not the tags because other fields are in strings and tags are an Array.
But now How can I modify this to work with the array as well.
I'm using that approach;
const handleTagsFilter = (value) => {
let updatedData = []
const dataToFilter = () => {
if (
status.length ||
custom_tags.length
) {
return filteredData
} else {
return results
}
}
setCustomTags(value)
if (value.length) {
updatedData = dataToFilter().filter((item) => {
const startsWith = item.status.toLowerCase().startsWith(value.toLowerCase())
const includes = item.status.toLowerCase().includes(value.toLowerCase())
if (startsWith) {
return startsWith
} else if (!startsWith && includes) {
return includes
} else return null
})
setFilteredData([...updatedData])
setCustomTags(value)
}
}
That function works with filtering strings like we have the status field to Active than this work, But I'm not sure how I can modify it to work with the array as well.
Maybe something like:
let search_str = 'abc'.toLowerCase();
let filtered_results = results
.map(v => v.tags.filter(_v => _v.val.toLowerCase().includes(search_str)))
.filter(v => v.length)
.reduce((a, b) => a.concat(...b), [])

How to solve "Expected to return a value in arrow function" error in eslint

I am using eslint and getting this error.
Expected to return a value in arrow function
The error is showing on the third line of the code.
useEffect(() => {
let initialPrices = {};
data.map(({ category, options }) => {
initialPrices = {
...initialPrices,
[category]: options[0].price,
};
});
setSelectedPrice(initialPrices);
}, []);
The map function must return a value. If you want to create a new object based on an array you should use the reduce function instead.
const reducer = (accumulator, { category, options }) => (
{...accumulator, [category]:options[0].price}
)
const modifiedData = data.reduce(reducer)
More information https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce
The map function is intended to be used when you want to apply some function over every element of the calling array. I think here it's better to use a forEach:
useEffect(() => {
let initialPrices = {};
data.forEach(({ category, options }) => {
initialPrices = {
...initialPrices,
[category]: options[0].price,
};
});
setSelectedPrice(initialPrices);
}, []);
Your map function should return something. Here it's not the case so the error happens. Maybe a reduce function will be more appropriate than map?
From what I can see in your case, is that you want to populate initialPrices, and after that to pass it setSelectedPrice. The map method is not a solution, for you in this case, because this method returns an array.
A safe bet in your case would a for in loop, a forEach, or a reduce function.
const data = [
{
category: "ball",
options: [
{
price: "120.45"
}
]
},
{
category: "t-shirt",
options: [
{
price: "12.45"
}
]
}
];
The forEach example:
let initialPrices = {};
// category and options are destructured from the first parameter of the method
data.forEach(({ category, options}) => {
initialPrices[category] = options[0].price;
});
// in this process I'm using the Clojure concept to add dynamically the properties
setSelectedPrice(initialPrices);
The reduce example:
const initialPrices = Object.values(data).reduce((accumulatorObj, { category, options}) => {
accumulatorObj[category] = options[0].price
return accumulatorObj;
}, {});
setSelectedPrice(initialPrices);

Functional JavaScript, function returns bool on condition mapping array

I need to push to the array cities if there is no such a city
getCityList() {
const { StoreInfo } = this.props;
this.emptyCityList();
return StoreInfo.map((StoreInfo, index) => {
console.log(this.cityPushCheck(StoreInfo.city));
if (this.cityPushCheck(StoreInfo.city)) {
CITY_LIST.push({
id: index, label: StoreInfo.city, value: StoreInfo.city, disabled: false
});
}
});
}
cityPushCheck(city) {
const MAP = CITY_LIST.map((CITY_LIST) => {
if (CITY_LIST.label === city) {
console.log('are equal');
return false;
}
});
return true;
}
I was trying return CITY_LIST.map((CITY_LIST) =>
without const or return CITY_LITS.map is not working
can't get it.
what I am doing wrong?
[UPDATE]
So this solution worked for me
getCityList() {
const { StoreInfo } = this.props;
this.emptyCityList();
return StoreInfo.map((StoreInfo, index) => {
if (this.cityPushCheck(StoreInfo.city)) {
CITY_LIST.push({
id: index, label: StoreInfo.city, value: StoreInfo.city, disabled: false
});
}
});
}
cityPushCheck(cityLabel) {
const cityFromList = CITY_LIST.find(city => city.label === cityLabel);
return cityFromList === undefined;
}
thanks to #quittle
It's a bit unclear what you are asking for exactly but I believe your question is how to implement cityPushCheck so that it returns false if the city is in CITY_LIST and true otherwise. Assuming CITY_LIST is an Array, there's a helpful function called find on arrays that greedily check for the presence of an entry based on a condition.
cityPushCheck(cityLabel) {
// Grabs the instance of the city from the list if it was present
const cityFromList = CITY_LIST.find(city => city.label === cityLabel);
// cityFromList will be undefined if "find" didn't have a match
return cityFromList === undefined;
}
While use map, you go city by city in your array, and for each return boolean. You might consider use filter, and then check if the filtered array have any length just once
cityPushCheck(city) {
CITY_LIST.filter(CITY => CITY.label == city).length
}
Now, if there isn't such city at array, length is 0 what is equal to false at JS

reactJs setState change property on object within an array

I'm a little bit stuck. I try to override a certain object from an array with another object from another array :
onStepsLayoutChange = (layout) => {
console.log("The new layout is", layout); //Array
this.setState((prevState) => ({
layout: layout,
stepsData: prevState.stepsData.map(step => {
layout.map(l => {
if(step.identifier === parseInt(l.i)){
console.log("Match", l, step); // "l" is not empty and actually matches only if "id" is identical
return {
...step,
layout: l //I want to override "layout" with the current layout "l"
}
}
});
return step
})
}), () => console.log("LayoutChange:", this.state.layout, this.state.stepsData)); // "layout" in each step is empty
};
Whats my fail in this case?
Issue is, you are missing the default behaviour of #array.map. For each array value map will return some value by default undefined. You are running map inside map, So the final value that stepData will have is:
[[...], [...], [....] ....]
Instead of using nested map, use #array.findIndex or #array.find and return the value.
Write it like this:
stepsData: prevState.stepsData.map(step => {
let index;
index = layout.findIndex(l => step.identifier === parseInt(l.i));
if(index >= 0){
return {
...step,
layout: layout[index]
}
}
return step;
})
Check this snippet:
let a = [1,2,3,4,5];
let b = [2,3];
let result = a.map(i => {
return b.map(j => {
if(i === j)
return 0;
})
return i;
})
console.log(result);
If you stepsData must be an array of arrays you forget one return:
onStepsLayoutChange = (layout) => {
console.log("The new layout is", layout); //Array
this.setState((prevState) => ({
layout: layout,
stepsData: prevState.stepsData.map(step => {
return layout.map(l => { //here <---
if(step.identifier === parseInt(l.i)){
console.log("Match", l, step); // "l" is not empty and actually matches only if "id" is identical
return {
...step,
layout: l //I want to override "layout" with the current layout "l"
}
}
});
return step
})
}), () => console.log("LayoutChange:", this.state.layout, this.state.stepsData)); // "layout" in each step is empty
};

Categories