Reputation: 5101
I am trying to filter the parent array based on the grand child array's values, but my filtering approach doesn't work well and the UI is not getting updated.
Here is my filter function.
filterIt($event) {
this.filter.RegCategoryName = $event.target.value;
this.dataObject = staticData.filter(value =>
value.List.filter(ch => ch.RegistrationCategory.filter(gChild =>
Object.keys(this.filter).every(ob =>
String(gChild[ob]).toLowerCase().includes(String(this.filter[ob]).toLowerCase())
)
)).length
)
}
Expectation:
When the user types new Course
, it should only show DP Programme
in Programme column.
Upvotes: 2
Views: 2397
Reputation: 8660
You can achieve this by using a combination of map
, filter
and some
Array
methods. Use the spread operator ...
so that your original array does not get updated in the child objects.
filterIt($event) {
this.filter.RegCategoryName = $event.target.value;
this.dataObject = staticData.filter(value => {
const data = { ...value };
data.List = data.List.map(ch => {
const list = { ...ch };
list.RegistrationCategory = list.RegistrationCategory.filter(gChild => {
return gChild.RegCategoryName.toLowerCase().indexOf(this.filter.RegCategoryName.toLowerCase()) !== -1
});
return list;
});
return data.List.some(list => !!list.RegistrationCategory.length);
});
}
Note: Avoid using variable names with the first letter capitalized.
Here is a working example on StackBlitz.
Explanation:
The structure used here is pretty complicated so I guess I'll start from inside out. Let's start out with the innermost filter
.
list.RegistrationCategory = list.RegistrationCategory.filter(gChild => {
return gChild.RegCategoryName.toLowerCase().indexOf(this.filter.RegCategoryName.toLowerCase()) !== -1
});
The
filter()
method creates a new array with all elements that pass the test implemented by the provided function.
This one should be clear enough. The condition used here is the same one used in Angular Material's mat-table
filter. This will filter if RegCategoryName
contains the search term. I say contain because this will match new Course
even if your search term is ew
. If this is not what you desire, you can use startsWith
instead. This will only match if your search term is new
and not ew
.
list.RegistrationCategory = list.RegistrationCategory.filter(gChild => {
return gChild.RegCategoryName.toLowerCase().startsWith(this.filter.RegCategoryName.toLowerCase())
});
Moving on to the first parent array List
. We do not want to filter
or change the objects in here. Instead, we just want the same object but with the new filtered RegistrationCategory
. This fits well for a map
since map
returns an array with the same number of objects(with some modification to the elements within the object if desired).
The
map()
method creates a new array with the results of calling a provided function on every element in the calling array.
So in our map
we will update RegistrationCategory
using the filter
we created above. We cannot modify ch
directly since that would mutate the original object. So instead we will shallow clone ch
using the spread syntax into a variable list
, modify RegistrationCategory
in our new list
variable using the above filter
and return the object. This now gives us the List
array as is by just updating RegistrationCategory
in each object.
Now for the final piece, we need to filter
through staticData
. As we did in the map
, we will use the spread syntax here again as we do not want to update the original object. data.List
will now have each RegistrationCategory
filtered thanks to our innermost filter, so we just need to return a true
/false
value based on whether the List
array has any object whose RegistrationCategory
length is more than 0. We can use some
for this.
The
some()
method tests whether at least one element in the array passes the test implemented by the provided function. It returns a Boolean value.
So if any RegistrationCategory
in the List
array has length more than 0(since that's all we care about, whether any RegistrationCategory
is still present after our innermost filter has filtered List
), it returns true
and is added to the filtered dataObject
.
This way we get each object in staticData
whose List
array contains an object where RegistrationCategory
has some data.
I hope this makes it clear why you were having issues with your code and why you cannot use a filter for all three arrays.
Upvotes: 2