In one component I can filter my array using the following:
// Array of product objects
const result = products.filter(p => p.name.includes('val'));
and value of products remains same as the first value but filtered value stores in result.
But in the following code, filter() filters array of strings itself:
// Array of strings
const result = strs.filter(s => s.includes('val'));
The question is how can I filter strings and return result without modifying the strs itself?
Note: I tried with array.filter(function() { return res; }); but didn't make any change.
It returns the filtered ones and don't change the actual array. You are doing something wrong
const strs = ['valval', 'bal', 'gal', 'dalval'];
const result = strs.filter(s => s.includes('val'));
console.log(strs);
console.log(result);
First thing we need to know is, if we filter our list we loose our original data
products: any[] = [
{
"productId": 1,
"productName": "foo-bar",
"price": 32.99
}
]
and can't get it back without re-getting the data from it's source so we have to make another list to store the filtered list.
filteredProduce: any[];
Next if you are working to show a list of filtered product on a grid or something like this we need a way to know when the user changes the filter criteria. we could use event binding and watch for key presses or value changes, but an easier way is to change our _listFilter property into a getter and setter, like this
get listFilter: string {
return this._listFilter;
}
set listFilter(value:string) {
this._listFilter= value;
}
next we want to set our filteredProducts array to the filtered list of products like this
set listFilter(value:string) {
this._listFilter= value;
this.filteredProducts = this._listFilter? this.performFilter(this._listFilter) : this.products;
}
in preceding code we are using js conditional operator to handle the posibility that _listFilterstring is empty, null or undefined.
Next we use this.performFilter(this._listFilter) to filter our list.
performFilter(filterBy: string): any[] {
filterBy = filterBy.toLocaleLowerCase();
return this.products.filter((product: any) =>
product.productName.toLocaleLowerCase().indexOf(filterBy) !== -1);
}
Finally we should assign the main list of products to the filteredProducts and _listFilter to what we want.
constructor() {
this.filteredProducts = this.products;
this._listFilter= 'foo-bar';
}
last step is to change our template to bind to our filteredProducts property.
Related
I'm making a menu app where people can filter food and drinks. It's divided in categories, and categories contain items. One of the properties is 'alcohol' -> does the drink contain alcohol?
I have a state-object with 2 arrays: an 'original' array (containig all items) and a 'filter' array (where the filtering happens).
Every time a filter is changed, I want to copy the original array and start filtering all over again. But it seems that after filtering one time, the original array is changed, and it's weird because I clone the array.
My code:
const [arrays, setArrays] = useState({
original: [{ catName: 'drinks', items: [{},{},...]}],
filter: [] // same as original in the beginning
});
useEffect(() => {
const copy = [...arrays.original];
copy.forEach((cat, idx) => {
if (!filter.alcohol) {
copy[idx].items = cat.items.filter(e => e.properties.alcohol === false);
}
});
setArrays(p => ({
...p,
filter: copy
});
}, [filter]);
I never 'touch' the original array, but after a filter changes, not only the 'filter'-array is changed (like expected), but also the original array.
Edit: expected behaviour:
when I filter on alcohol, I'm expecting that all drinks containing alcohol are removed from the filter-array. This works, but when I remove the filter, all the alcohol-drinks should be back in the filter-array. This doesn't work because they are also deleted from the original array.
Sounds like you may just be returning a shallow copy of the original array. Have you tried Lodash's clonedeep?
My issue is that when i call the function getAllFlashCardsFromQuest(), its call all flash cards ever created in all the pages, i wanna only the objects that comes from a specific pathname, or a way to filter the cards array.
async function getCards() {
let cardsValues = await getAllFlashCardsFromQuest() as FlashCard[]
let cardsFiltered = cardsValues.filter(()=>{
return history.location.pathname === 'CriarAlternativaQuest'
})
console.log(cardsFiltered)
setCards(cardsFiltered)
}
the object look like this:
There's not much context with your code, so I can only help so much, but one thing's for sure, and that is history.location.pathname isn't changing and you're comparing it with another constant. So cardsFiltered will be either a list of true or a list of false.
Whenever you're filtering a list, you need to take each item or a property of each item and use that in your comparison.
Ex.
async function getCards() {
let cardsValues = await getAllFlashCardsFromQuest() as FlashCard[]
let cardsFiltered = cardsValues.filter((cardValueItem) => {
return cardValueItem.pathname === 'CriarAlternativaQuest'
})
}
The thing is that you need to figure out what value or property inside of your cardsValues list that you need to compare it with 'CriarAlternativaQuest'
I'm making a React-Native application. Thanks to everyone's help I could somehow make that work except for toggling YES and NO. Once the user clicks on a button I just want to check if that clicked item data already exists in the state, if so I want to update it. If it does not exist then it should be added to the state as a new Item.
I already coded the above logic, my code is working, but not returning correct output, elements are not adding or updating to the state array properly. How do I fix this code?
I want to generate output like this
[{
chelistid:231,
validiary : "YES",
remark : "Hello"
},{
chelistid:232,
validiary : "NO",
remark : "asddddd"
}]
My code
const [reqData, setReqData] = useState([]);
//Modify yes clicked
const yesClicked = (element) => {
let req = {
"chelistid": element.chelistid,
"validiary": "Yes",
"remark": element.remark
}
createCheckList(req);
}
//Modify no clicked
const noClicked = (element) => {
let req = {
"chelistid": element.chelistid,
"validiary": "No",
"remark": element.remark
}
createCheckList(req);
}
const createCheckList = (data) => {
const index = reqData.findIndex(x => x.chelistid === data.chelistid)
var modifiedArray = reqData
if (index !== -1) {
//Remove the element from the array
modifiedArray.splice(index, 1);
}
setReqData([modifiedArray, data]);
}
The problem is it seems like you are not spreading the array to append the data element. What you are doing by [modifiedArray, data] you are creating an array that contains an array and data something like [[modified array content here], data]. But actually, you want to append to modified array instead. For that, you need to expand the modified array by using ... which is called spread syntax. (Learn here) So, your code would look like:
setReqData([...modifiedArray, data]);
I'm using ng-repeat to (guess) put array content in table.
Content is drawn dynamically, and it works well, when I'm modifying single elements of an array. But when I reload a whole array, there is this moment, when array is reassigned with new value, and ng-repeat draws blank table (which is actually logically correct). Is there a way to delay redrawing of content that way, the ng-repeat ignores the moment when the array is empty? Like the content is switched to new content without the 'clear' time.
I'm assigning new elements to array this way:
items = newItems;
where items is the array ng-repeat uses and newItems is an array of items freshly downloaded from database. The newItems is complete, when the assignment occurres. I'm not doing items = []; before the assignemt.
I'm usign angular 1.3
EDIT:
the ng-repeat:
<tr ng-repeat="order in submittedOrders">
stuff
<\tr>
js:
`$scope.reloadView = function() {
$scope.submittedOrders = OrdersService.getOrdersByStatus(ORDER_STATUS.submitted);
};`
Can it be the that the table is cleared in the first place, before call to database(service takes data from database) and during the wait, the table is cleared?
You may have to make use of Observables and async pipe of Angular.
Here are few steps you can take:
Convert your newItems to a rxjs Subject.
newItems$ = new Subject();
Whenever you get new values for your array, emit them via subject.
this.newItems$.next(newItems);
Make the items an observable of newItems$, and filter out empty arrays.
items = this.newItems$.pipe(
filter((a:any[]) => {
return a.length != 0;
})
);
In your template, use async pipe to iterate over array.
*ngFor="item of items | async"
Below is relevant parts of code that can get you started.
import { Observable, of, from, Subject } from 'rxjs';
import { filter, mapTo } from 'rxjs/operators';
...
newItems$ = new Subject();
items = this.newItems$.pipe(
filter((a:any[]) => {
return a.length != 0;
})
);
...
// A test method - link it to (click) handler of any div/button in your template
// This method will emit a non-empty array first, then, after 1 second emit an empty
// array, and then, after 2 seconds it will emit a non-empty array again with updated
// values.
testMethod() {
this.newItems$.next([3,4,5]);
setTimeout((v) => {
console.log("Emptying the array - should not be displayed browser");
this.newItems$.next([]);
}, 1000);
setTimeout((v) => {
console.log("Updating the array - should be displayed in browser");
this.newItems$.next([3,4,4,5]);
}, 2000);
}
I have a prop that is a list of objects. My reducer is meant to update a certain field of the object and append it to the list, the value to update is provided via action.payload (which is another prop of the store).
I know that for simply adding the object in the list I can use the spread operator like this
function myReducer(state=[],action){
case something:
return [...state,action.payload];
case default:
return state;
}
but say I have a change action.payload.aCertainField and then append this to the list of objects. When I did something like this:
action.payload.aCertainField = aCertainValue;
return [...state,action.payload];
But it actually changed the other prop's aCertainField as well. I do not want that. Is there a solution to this ?
You can do it as following, using ES6:
let { aCertainField, id } = action.payload.obj;
//find the index of object in that array hoping the id is unique for the object
let indexOfObject = state.findIndex( (item) => item.id === id );
let actualObject = Object.assign({}, state[indexOfObject], { aCertainField });
return [
...state.slice(0, indexOfObject),
actualObject,
...state.slice(indexOfObject+1)
]
NOTE: Thinks that id is the unique key for the object structure and aCertainField is the value to update