I have a little problem with my filter function. I have a data structure as shown in the image below.
As you can see, I have an array of objects named bridals and it keeps another array of objects named plans. So inside that I am trying to filter people.
Here is the filter function.
export function peopleFilter(bridals, people){
if (people.length === 0) {
return bridals;
} else {
bridals.forEach(bridal => {
bridal.plans.filter((item) => {
if (item.people === people) {
return bridals;
}
})
})
return bridals;
}
}
The function peopleFilter() should filter plans with selected value only and return with bridals, but it returns nothing. No error is shown as well.
So I tried something like below.
bridals.forEach(bridal => {
bridal.plans.slice().reverse().forEach((item, index, object) => {
if (item.people !== people) {
bridal.plans.splice(object.length - 1 - index, 1)
}
})
})
return bridals;
This above code is doing what I want. But there is one problem. So in the end when I select no value, it should display all plans. But it doesn't because I already removed the plans using splice() every time I select some value.
So I am stuck about this. How can I fix it?
In this case it might be easier to iterate over your bridal array with a map. Within the map, filter plans for each bridal, then return a new obj, which can be accomplished with spread syntax.
const filterFunc = (bridals, people) => {
return bridals.map(bridal => {
const filteredPlans = bridal.plans.filter(plan => plan.people === people);
return { ...bridal, plans: filteredPlans };
})
}
Let's say you want to know the people count for all bridals and plans:
var total = 0;
bridals.forEach(function(b){
b.plans.forEach(function(o){
total += o.people;
});
});
Of course, I can't really tell what you're trying to do. I could write an entire API for this.
You need to return a boolean value inside the filter() instead of return bridals;. Additionally, this returned array needs to be reassigned to bridal array as well.
export function peopleFilter(bridals, people) {
if (people.length === 0) {
return bridals;
} else {
bridals.forEach(bridal => {
bridal = bridal.plans.filter((item) => {
return item.people === people;
})
})
return bridals;
}
}
Related
Im making a function which compares two arrays of objects and returns two subset arrays of objects. The first returned array being a set of objects which were not included within the array passed within the second prop, and the second returned array being a set of objects which were not included within the first array but are now included within the second.
This works, however, i want to add an extra functionality to return the second array with specific keys. Is there anyway to utilise lodash "pickBy" functionality within the filter function, or will i have to create an extra recursive section?
function extract_removed_new (before, after, matching, pickBy = []) {
let before_ = cloneDeep(before)
let new_ = _.filter(after, (item) => {
let index_ = _.findIndex(before_, function (i) {
return _.get(i, matching) == _.get(item, matching)
})
if(index_ !== -1) {
before_.splice(index_, 1)
return false
}
if(pickBy.length > 0) {
return _.pickBy(item, function(value, key) {
if(key in pickBy) {
return true
}
});
}
return true
})
return [before_, new_]
}
Using lodash compact, with map I was able to get exactly what I needed.
function extract_removed_new (before, after, matching, pickBy = []) {
let before_ = cloneDeep(before)
let new_ = _.compact(_.map(after, (item) => {
let index_ = _.findIndex(before_, function (i) {
return _.get(i, matching) == _.get(item, matching)
})
if(index_ !== -1) {
before_.splice(index_, 1)
return false
}
if(pickBy.length > 0) {
return _.pickBy(item, function(value, key) {
if(pickBy.includes(key)) {
return true
}
});
}
return item
}))
return [before_, new_]
}
I was working on a hackerrank problem and test cases showed that something was wrong with my 'remove' method. I always got undefined instead of true/false.
I know splice returns array of deleted elements from an array. When I console.log inside map, it looked like everything was fine when I was deleting first element (I was getting what I expected except true/false). But when 'name' I am deleting is not first element, I didn't get what I expected to get. Could you help me fix this? And of course, I never get true or false...
class StaffList {
constructor() {
this.members = [];
}
add(name, age) {
if (age > 20) {
this.members.push(name)
} else {
throw new Error("Staff member age must be greater than 20")
}
}
remove(name) {
this.members.map((item, index) => {
if(this.members.includes(name)) {
console.log(name)
let removed = this.members.splice(item, 1);
console.log(removed)
return true;
} else {
return false;
}
})
}
getSize() {
return this.members.length;
}
}
let i = new StaffList;
i.add('michelle', 25)
i.add('john', 30);
i.add('michael', 30);
i.add('jane', 26);
i.remove('john');
console.log(i);
Your return statements are wrapped within .map() (which you misuse in this particular case, so you, essentially, build the array of true/false), but your remove method does not return anything.
Instead, I would suggest something, like that:
remove(name){
const matchIdx = this.members.indexOf(name)
if(matchIdx === -1){
return false
} else {
this.members.splice(matchIdx, 1)
return true
}
}
In the remove method, you're using map with the array, which runs the function you give as argument for each array element. But I believe you don't want to do that.
Using the example you have bellow, basically what you do there is check if the array contains the name 'john', and if so, you delete the first item that appears in the array (which would be 'michelle'). This happens because the map function will run for every element, starting on the first one, and then you use that item to be removed from the array. After that, it returns the function, and no other elements get removed.
So my suggestion is just getting rid of the map function and running its callback code directly in the remove method (you would need to get the name's index in the array to use the splice method).
It is not clear why you need to use iterative logic to remove an item. You can simply use findIndex() to get the position of the member in the array. If the index is not -1, then you can use Array.prototype.slice(index, 1) to remove it. See proof-of-concept example below:
class StaffList {
constructor() {
this.members = [];
}
add(name, age) {
if (age > 20) {
this.members.push(name)
} else {
throw new Error("Staff member age must be greater than 20")
}
}
remove(name) {
const index = this.members.findIndex(x => x === name);
if (index !== -1) {
this.members.splice(index, 1);
}
}
getSize() {
return this.members.length;
}
}
let i = new StaffList;
i.add('michelle', 25)
i.add('john', 30);
i.add('michael', 30);
i.add('jane', 26);
i.remove('john');
console.log(i);
Use a filter method instead of map it's more elegant and you can return the rest of the array as well instead of true or false unless the problem you're working on requires true of false specifically.
You could write something like this:
remove(name) {
if (!this.members.some(el => el === name)) return false;
this.members = this.members.filter(item => item !== name);
return true;
}
I would like to return an updated array to state, so I am using map with filter as follows:
state.assignmentClusters.map(cluster => {
cluster.files.forEach(file => {
file.motifList.filter(motif =>{
return motif.motif_id !== state.motifIdToDelete;
});
});
});
Since I need to return an array, I am using map() on first array, the 2nd one (file) i'm just going over it without expecting to mutate anything, so I used forEach() and in the last depth, I am using filter() to return all motifs except for those that their id is equal to state.motifIdToDelete (just fyi, both IDs are strings).
PS, this piece of code is within a Vue mutation function that allows me to mutate state (like reducer in react).
What am I doing wrong here?
Thanks,
bud.
I have solved this differently yet I would like to grant the answer to the person who can show me how to preform this correctly with filter()
this currently works:
state.assignmentClusters.forEach(cluster => {
cluster.files.forEach(file => {
file.motifList.forEach((motif, index) =>{
if (motif.motif_id == state.motifIdToDelete) {
file.motifList.splice(index, 1)
}
});
});
});
Your call to state.assignmentClusters.map returns nothing. You should return cluster in the arrow function.
Also, your call to file.motifList.filter doesn't mutate anything. You must use the value returned by the filter function somewhere.
state.assignmentClusters.map(cluster => {
cluster.files = cluster.files.map(file => {
file.motifList = file.motifList.filter(motif => {
return motif.motif_id !== state.motifIdToDelete;
});
return file;
});
return cluster;
});
Another option if mutation doesn't matter:
state.assignmentClusters.forEach(cluster => {
cluster.files.forEach(file => {
file.motifList = file.motifList.filter(motif => {
return motif.motif_id !== state.motifIdToDelete;
});
});
});
I have a checkbox group that I want to get all checked items. I am trying to pass an Array to a function so I can get all checked items but it's not working.
checkedCategory: Array<number>;
contains(checkedArr: Array<number>, id: number): boolean {
if (checkedArr instanceof Array) {
return checkedArr.indexOf(id) > -1;
} else if (!!checkedArr) {
return checkedArr === id;
}
return false;
}
private add(checkedArr: Array<number>, id: number) {
if (!this.contains(checkedArr, id)) {
console.log('add: ' + checkedArr);
if (checkedArr instanceof Array) {
checkedArr.push(id);
} else {
checkedArr = [id];
}
}
}
private remove(checkedArr: Array<number>, id: number) {
const index = checkedArr.indexOf(id);
if (!checkedArr || index < 0) {
return;
}
checkedArr.splice(index, 1);
}
toggleCategory(id: number) {
if (this.contains(this.checkedCategory, id)) {
this.remove(this.checkedCategory, id);
} else {
this.add(this.checkedCategory, id);
}
}
I have a (click) event in my checkbox that will call togglecategory
(click)="toggleCategory(category.id)"
Then, when I try to console.log the 'checkedCategory' it's undefined.
I have 3 checkboxes group and I want to reuse the 'contains/add/remove' function that's why I want to pass an array.
Thank you
When you call toggleCategory(20) see what happens, in your case you will see that your function will print add: undefined. so the first thing you must debug is your add function. I think the issue is that your array is not defined. Try to initalize your empty array like this let checkedCategory: Array<number> = Array();
But either way, You need to debug your add function. Good Luck :)
If you have any questions about why this is the solution, let me know, I dont mind sharing the Theory aspect to why this occurs if you are interested.
It's driving me crazy. I've created a list with several entries. I added a filtering function, which seems to work fine. I've checked the number of results returned, but somehow it just showing the result number beginning at the first row.
For explanation:
Let's assume I search for "Zonen" and my filter function returns 4 rows with ID 23, 25, 59 and 60, the rows with ID's 1,2,3 and 4 are displayed. What I'm doing wrong!?
...
render() {
let filteredList = this.state.freights.filter((freight) => {
let search = this.state.search.toLowerCase();
var values = Object.keys(freight).map(function(itm) { return freight[itm]; });
var flag = false;
values.forEach((val) => {
if(val != undefined && typeof val === 'object') {
var objval = Object.keys(val).map(function(objitm) { return val[objitm]; });
objval.forEach((objvalue) => {
if(objvalue != undefined && objvalue.toString().toLowerCase().indexOf(search) > -1) {
flag = true;
return;
}
});
}
else {
if(val != undefined && val.toString().toLowerCase().indexOf(search) > -1) {
flag = true;
return;
}
}
});
if(flag)
return freight;
});
...
<tbody>
{
filteredList.map((freight)=> {
return (
<Freight freight={freight} onClick={this.handleFreightClick.bind(this)} key={freight.id} />
);
})
}
</tbody>
...
UPDATE
freights is loaded and filled via AJAX JSON result. One object of freights looks like this:
I have a textbox where a user can perform a search. This search should return all freight objects which properties contain the search string.
The filter is so complex, because I want to also to search in sub-objects of freight. Maybe there is a more simple way?
"Zones" was just an example for a search string the user can search for.
Now that your intentions are clearer, I suggest this much less complex solution.
First, you can write a recursive utility fn to get all values of all keys in an n-depth object. Like this, for example (I'm using lodash's utility fn isObject there):
const getAllValues = (obj) => {
return Object.keys(obj).reduce(function(a, b) {
const keyValue = obj[b];
if (_.isObject(keyValue)){
return a.concat(getAllValues(keyValue));
} else {
return a.concat(keyValue);
}
}, []);
}
Now that you have an array of all object's values, it makes your filter very simple:
let filteredList = this.state.freights.filter((freightItem) => {
const allItemValues = getAllValues(freightItem);
return allItemValues.includes(this.state.search);
});
That should be it. If something is not working, gimme a shout.
I have found the solution why the "wrong" freight entries are displayed.
I needed to add in freight component the componentWillReceiveProps method:
componentWillReceiveProps(nextProps) {
if(nextProps.freight) {
this.setState({
freight: nextProps.freight
});
}
}
Then everything worked fine.