I'd like to filter an array of objects based on multiple tests. For this example, I want to filter an array of objects if the values for the keys aren't null, and that one value for one key is less than 90. I'm currently doing this with a for loop like so:
let filtered = []
for (let i = 0; i < articles.length; i++) {
if (articles[i].title !== null && articles[i].title.length <= 90 &&
articles[i].date !== null &&
articles[i].image !== null &&
articles[i].description !== null &&
articles[i].link !== null) {
filtered.push(articles[i])
}
}
But it's pretty clumpy and I know the filter method can achieve something similar. But I'm unsure if it can check multiple keys and their values with the same test whilst checking if a specific value passes an independent test too.
Try:
articles.filter(article =>
Object.values(article).every(x => (x !== null))
&& article.title.length <= 90
)
Let's break this down:
articles.filter(article => ...)
.filter is a function property of type that accepts a callback argument, which it calls for each item. Essentially, we're passing it a function - not executing it right away, which it can call at its leisure. It's sort of like:
let a = alert;
We're not calling the alert function, we're just saving it to a variable. In the case of .filter, we're using it as a pseudo-variable - an argument. Internally, all .filter is doing is:
Array.prototype.filter(callbackFunc) {
newArr = [];
for (i=0;i<this.length;i++){
if (callbackFunc(this[i]) === false){ // We're calling `callbackFunc` manually, for each item in the loop.
newArr.push(this[i]);
}
}
return newArr;
}
The next bit to explain is the actual callback function we're using. It's defined with ES6 arrow syntax, but it's the equivalent of:
articles.filter(function(article){
return Object.values(article).every(x => (x !== null))
&& article.title.length <= 90
})
The first line of the callback function, Object.values(article).every(x => (x !== null)), can be broken down to:
let values = Object.values(article); // Get the value of all of the keys in the object
function checkFunction(item){ // Define a helper function
return (x !== null); // Which returns if an item is _not_ null.
}
let result = values.every(checkFunction); // Run `checkFunction` on every item in the array (see below), and ensure it matches _all_ of them.
Finally, we just need to clarify what every does. It's another example of functional JS, where functions accept callback functions as parameters. The internal code looks like this:
Array.prototype.every(callbackFunc) {
for (i=0;i<this.length;i++){
if (callbackFunc(this[i]) === false){ // We're calling `callbackFunc` manually, for each item in the loop.
return false;
}
}
// In JS, returning a value automatically stops execution of the function.
// So this line of code is only reached if `return false` is never met.
return true;
}
And && article.title.length <= 90 should hopefully be self-explanatory: while .every returns a boolean value (true or false), a true will only be returned by the callback function to the filter if the second condition is also met, i.e if the every returns true and article.title.length <= 90
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With