remove an object from an array is not working properly ,but it add object perfectly
const addItem =(selected)=>{
let data = selectedItems ? [...selectedItems] : [];
if (data.length) {
let index = data.indexOf(selected);
console.log(index);
if (index !== -1) {
data.splice(index, 1);
setSelectedItems(data);
} else {
data.push(selected);
}
} else {
data.push(selected);
}
console.log("selected", selectedItems);
setSelectedItems(data);
}
render button function add or remove on click it
<div className="file-list">
<MappedElement
data={[{ _id: 1 }, { _id: 2 }]}
renderElement={(value, index, arr) => {
let check=selectedItems.some((obj) => obj._id === value._id);
console.log("check", check);
return (
<DocumentCard key={index} className={file-list-item ${check ?
"active" : ""}}
onClick={() => addItem(value, arr, index)} /> ); }} />
</div>
For a selectedItems array that looks like:
const selectedItems = [
{ _id: 1, /* other fields */ },
{ _id: 2, /* other fields */ },
{ _id: 3, /* other fields */ },
/* other objects */
];
And a selected object that looks like:
const selected = { _id: 1 };
In order to perform the desired behavior, which is: if the element exists, remove it, else, add it, you can write the following:
// copy selected items with a fail-safe empty array
const data = selectedItems ? [...selectedItems] : [];
// find index of selected element
const removalIndex = data.findIndex(({ _id }) => (_id === selected._id));
// if selected element exists in data array, remove it
if (removalIndex !== -1) {
data.splice(removalIndex, 1);
}
// if selected element doesn't exist in data array, add it
else {
data.push(selected);
}
// update selected elements
setSelectedItems(data);
NOTE: if your array of selected items contains duplicates, meaning multiple objects that contain the same value for _id, then this approach will be removing only the first instance of those. If you want to remove all of them, you'll have to use a loop or recursivity.
Your problem is probably in indexOf method you're using.
You can not use this to find objects in your array.
There are several options you can use. You can use find or findIndex and pass a callback to find an object by the specified (preferably unique property of the object).
Example
let found = data.findIndex(item => item.id === selected.id);
Use
const addItem =(selected)=>{
let data = selectedItems ? [...selectedItems] : [];
if (data.length) {
let index = data.findIndex(value => value._id === selected._id)ж
console.log(index);
if (index !== -1) {
data.splice(index, 1);
} else {
data.push(selected);
}
} else {
data.push(selected);
}
console.log("selected", selectedItems);
setSelectedItems(data);
}
Related
I have an array of objects... each object has a boolean key called 'included' changed when clicking on a button in html
I need the array to be sorted the objects with true value on the top and then the objects with false value including that the last changed object with false value put at the last of array and if changed to true put after the objects with truthy value
the below is what I did ... is that optimum or there ara another way?
<mat-icon *ngIf="product.included"
(click)="applyAction('exclude', product.id)">
remove
</mat-icon>
<mat-icon *ngIf="!product.included"
(click)="applyAction('include', product.id)">
add
</mat-icon>
applyAction(action: action, id: number): void {
const selectedProduct = this.productTypes.find((i) => i.id === id);
if (action === 'exclude') {
selectedProduct.included = false;
} else {
selectedProduct.included = true;
}
this.productTypes = this.productTypes.filter((i) => i.id !== id);
this.productTypes.push(selectedProduct);
this.productTypes.sort((a, b) => Number(b.included) - Number(a.included));
}
Not sure about optimum but you could use .findIndex and use .splice remove the element and then do a .push to push the latest edited element to the end:
let productTypes = [{id: 1, included: true}, {id: 2, included: false}, {id: 3, included: true}];
const applyAction = (action, id) => {
const selectedProductIdx = productTypes.findIndex((i) => i.id === id);
const selectedProduct = productTypes[selectedProductIdx];
if (action === 'exclude') {
selectedProduct.included = false;
} else {
selectedProduct.included = true;
}
productTypes.splice(selectedProductIdx, 1);
productTypes.push(selectedProduct);
}
applyAction('exclude', 1);
applyAction('include', 2);
console.log(productTypes);
You can avoid the filter and push calls by retrieving the index of the product and avoid updating if either the product is not present or the value doesn't need a change.
Worst case scenario this function will traverse the productTypes array once for findIndex and sorting is n * logn:
applyAction(action: action, id: number): void {
const productIndex = this.productTypes.findIndex((i) => i.id === id);
if (productIndex === -1) return; // Product not found
const included = this.productTypes.included;
if ((included && action !=== 'exluded') || (!included && action === 'excluded')) {
// No update needed
return;
}
this.productTypes[productIndex].included = !included;
this.productTypes.sort((a, b) => Number(b.included) - Number(a.included));
}
I would say use one temporary array and push all the element except the particular last updated id element and end of the function push the last updated id element. It will took O(n) time and space.
applyAction(action: action, id: number): void {
let findInd = -1, temp_arr=[];
this.productTypes.forEach((element, index)=>{
if(element.id !=id) temp_arr.shift(element);
else findInd = index;
});
temp_arr.push(this.productTypes[findInd]);
this.productTypes = temp_arr;
}
Brief example:
piece of state: ['youth', 'college'];
event Object:
{ name: theEvent,
ageDivisions: {
youth: true,
middleSchool: true,
highSchool: true,
college: true
open: false
},
}
What I want to accomplish
I want to filter through multiple event objects and only return when the event at least contains all the matching strings in the state array. this array is created based on what the user selects from the form (image Below)
My approach so far:
So far my approach has been to turn the events age divisions parameter into an array of arrays that contain the key and the value. Positions 1 and 2 respectfully
Here is the code:
codePenLink
if (filterParamaters.ageDivisions !== undefined && filterParamaters.ageDivisions.length != false) {
let parsedEvents = events.data.map(({ attributes: event }) => {
return {
...event,
...event.attributes,
ageDivisions: JSON.parse(event.ageDivisions),
eventStyles: JSON.parse(event.eventStyles),
}
})
console.log({ parsedEvents });
filteredEventss = parsedEvents.filter((event) => {
// make event .attributes.ageDivisions object an array of key value pairs
console.log({ eventThatWeAreFiltering: event });
if ((event.ageDivisions !== undefined && event.ageDivisions !== null)) {
let eventAgeDivisions = Object.entries(event.ageDivisions);
console.log({ theAgifiedObject: eventAgeDivisions });
// This maps through each object in the Object key value pair Array
// It might be easier to use map and just chang the array to only have the matching values
let onlyTrueEventAgedivisions = eventAgeDivisions.map((ageDivision, index) => {
console.log({ theAgeDivision: ageDivision[2] });
if (ageDivision[2] === true) {
return [ageDivision[0], ageDivision[2]];
} else {
return false;
}
})
console.log({ theTrueEventAgedivisions: onlyTrueEventAgedivisions });
}
})
console.log({ theFinishedFilteredevents: filteredEventss });
}
What I did was check if the ageDivisions existed in each object and if it does, run this filterParameters.ageDivisions list, checking if every property that you want to be true is set to true .
const result = parsedEvents.filter((e) => e.ageDivisions && filterParameters.ageDivisions.every(prop => e.ageDivisions[prop]))
console.log(result);
I have a list like this :
[EMP004: BPCE-RNHC-25G8, EMP003: 8FIW-9JRB-NY4J, EMP005: 7QF2-6HI9-XKZZ, EMP002: SG8P-YQKG-ZV3C, EMP001: PBF7-WZHT-WPZR]
What I trying to achieve:
User input the first key and then the system will scan through the list and show them corresponding key. For example, if user input "EMP004", it should alert BPCE-RNHC-25G8 for them.
Idea:
How can I separate the first key and second key with : ? Other than that how can I know find the corresponding result? Does anyone know how can i solve this with JavaScript only ? Thanks in advance.
The sample list you have provided is syntactically incorrect. You can create an array object. Then you can create function that takes the key as the parameter like the following way:
const list = [
{ EMP004: 'BPCE-RNHC-25G8' },
{ EMP003: '8FIW-9JRB-NY4J' },
{ EMP005: '7QF2-6HI9-XKZZ' },
{ EMP002: 'SG8P-YQKG-ZV3C' },
{ EMP001: 'PBF7-WZHT-WPZR' }
];
const getValByKey = (key) => Object.values(list.find(o => Object.keys(o)[0] === key ))[0];
console.log(getValByKey('EMP004'));
console.log(getValByKey('EMP002'));
Update: For string items you can try the following way:
const list = ['EMP004: BPCE-RNHC-25G8', 'EMP003: 8FIW-9JRB-NY4J', 'EMP005: 7QF2-6HI9-XKZZ', 'EMP002: SG8P-YQKG-ZV3C', 'EMP001: PBF7-WZHT-WPZR'];
const getValByKey = (key) => list.find(item => item.split(':')[0].trim() === key ).split(':')[1].trim();
console.log(getValByKey('EMP004'));
console.log(getValByKey('EMP002'));
You can do like this:
function search(arr, sub) {
sub = sub.toLowerCase();
matchedIndex = arr.map((str, index) => {
if (
str.toLowerCase().startsWith(sub.slice(0, Math.max(str.length - 1, 1)))
) {
return index;
}
});
if(matchedIndex.length>0){
console.log(matchedIndex);
matchedIndex = matchedIndex.filter(function( element ) {
return element !== undefined;
});
return matchedIndex[0]
}
}
var arr = ["EMP004: BPCE-RNHC-25G8", "EMP003: 8FIW-9JRB-NY4J", "EMP005: 7QF2-6HI9-XKZZ", "EMP002: SG8P-YQKG-ZV3C", "EMP001: PBF7-WZHT-WPZR"];
var index = search(arr, 'EMP004');
if(index>-1){
matchedvalue=arr[index].split(':');
console.log(matchedvalue[1]);
}
else{
console.log("Not found")
}
I have a grid that shows the list of time (from time to time)
for example
timeList=[{from:12:00:00 , to:14:00:00 ,id:10}{from:08:00:00 , to:09:00:00 ,id:11{from:05:00:00 , to:10:00:00 ,id:12}}]
time=[{Value:12:00:00 id:10}
{Value:14:00:00 id:100}
{Value:08:00:00 id:11}
{Value:09:00:00 id:110}
{Value:05:00:00 id:12}
{Value:10:00:00 id:15}
]
To delete an item I have this code
deleteTimeView(data) {
////date ==>{from:12:00:00 , to:14:00:00 ,id:10}
let indexOfDeleted = -1;
let time = this.state.time;
let timeList=this.state.timeList;
this.state.time.forEach((item, index) => {
if (item.Id === data.id) {
indexOfDeleted = index;
}
})
time.splice(indexOfDeleted, 1);
time.splice(indexOfDeleted, 1);
timeList.splice(indexOfDeleted, 1);
this.setState({
time: time,
timeList: timeList
});
}
My problem is that when I want to delete id12, I have index 4, but I do not have this index in timeList.
I have no problem deleting that item in time, but how can I delete it from timeList as well
Firstly, don't use .splice, it mutates the array and in this case means you're directly mutating state. Never mutate the state in React.
Secondly, a simple filter approach based on the ID should satisfy your needs:
deleteTimeView(data) {
this.setState({
time: this.state.time.filter(t => t.id !== data.id),
timeList: this.state.timeList.filter(t => t.id !== data.id)
});
}
I'm trying to implement a search function that returns find which has search word in the specified array. Let's say, a collection has [aa, ab, aaa], and search word is "a". In this case, return the object to display. Because at least one of the strings in the array has 'a'.
dataStructure
[
{
name:'aa',
searchWords:['aa','ab','bc'] <- I want to use this array for search
},
{
name:'bb',
searchWords:['bb','bc','de'] <- I want to use this array for search
},
...
]
I tried to fix the issue, by using includes(), filter(),indexOf(). However still it returns nothing or returns data when the search word is exactly matched.
How to fix the code to achieve aiming?
Thank you in advance!
this part works well
let filterStudents = students;
if (searchName.length > 0 && searchTag.length === 0) {
filterStudents = students.filter((student) => {
if (
student.firstName.toLowerCase().includes(searchName.toLowerCase())
|| student.lastName.toLowerCase().includes(searchName.toLowerCase())
) {
return true;
}
return false;
});
Problem happens on this part
} else if (searchName.length === 0 && searchTag.length > 0) {
filterStudents = students.filter(
(student) => {
console.log(student.tags);
student.tags.filter((tag) => {
console.log(tag);
tag.indexOf(searchTag) > -1;
});
},
);
} else if (searchName.length > 0 && searchTag.length > 0) {
} else {
console.log('both');
}
You don't return from the filter callbacks
As a sidenote, there is also String#includes:
filterStudents = students.filter(student =>
student.tags.some((tag) => tag.includes(searchTag))
);
If you only want to search for matches when the corresponding searchTag or searchName is filled out, use the conditional operator inside the filter callback to check whether the filter test should be carried out:
const students = [
{
name:'aa',
searchWords:['aa','ab','bc']
},
{
name:'bb',
searchWords:['bb','bc','de']
},
];
const doFilter = () => {
const [searchName, searchTag] = [...document.querySelectorAll('input')]
.map(input => input.value.toLowerCase());
const filtered = students.filter(({ name, searchWords }) => (
(searchName ? name.toLowerCase().includes(searchName) : true) &&
(searchTag ? searchWords.some(word => word.toLowerCase().includes(searchTag)) : true)
));
code.textContent = JSON.stringify(filtered);
};
window.addEventListener('change', doFilter);
<input placeholder="searchName">
<input placeholder="searchTag">
<div>
<code id="code"></code>
</div>