Here's a trick I often find helpful.
Bad array. Very, very bad.
You have an array of whatever:
const array = [{ stuff }, { moreStuff }, ...]
But hiding in that array are some unusable null
or undefined
values:
const array = [{ good }, null, { great }, undefined]
Those empty values might be a sneaky little gift from an API. Or you may have left them there yourself. Either way, you've got a problem.
Looping over null data
If you try to perform the same actions on every item in the array, you'll run into errors when you hit those null
and undefined
items:
const newArray = array.map(item => {
// Of course this will work, wheeee...
const assumption = item.thing
})
// Oh noooo...
🚨 Cannot read property "thing" of undefined. 🚨
🚨 Cannot read property "thing" of null. 🚨
Illegal! Now you're a criminal. Before you interact with an item, you need to make sure it exists.
Null checks?
You could confirm each item exists by performing a null
check before you use it:
const newArray = array.map(item => {
// Life has made me cautious.
if (!item) {
return item // Just forget it
}
// If we get this far, item exists.
const assumption = item.thing
})
Buuut, now your code is getting cluttered. And worse, those dangerous empty values will be passed along to newArray
. So when you use newArray
, another round of suspicious null
checks will be needed.
The truth and only the truth
Want something better?
Here's my favourite way to quickly remove all empty items from an array:
const array = [{ good }, null, { great }, undefined]
const truthyArray = array.filter(Boolean)
// truthyArray = [{ good }, { great }]
The filter(Boolean)
step does the following:
- Passes each item in the array to the Boolean constructor
- The
Boolean
constructor coerces each item totrue
orfalse
depending on whether it's truthy or falsy - If the item is truthy, we keep it
Where did item go?
I love how concise filter(Boolean)
is, but it might look strange that we aren't explicitly passing item
to Boolean
.
The main thing to know is that, in JavaScript, this:
array.filter(item => Boolean(item))
is exactly the same as this:
array.filter(Boolean)
The second version is just written in a "tacit" or "point-free" style. We don't name each item and pass it into Boolean
, but JavaScript understands that Boolean
takes one argument, so it takes the argument filter()
exposes and passes it to Boolean for you.
If you find the first version easier to understand, use it! Readable code is more important than fancy tricks.
Safer mapping
With our new tool, we can remove the null
checks from above and chain a filtering step instead:
const newArray = array.filter(Boolean).map(item => {
// Item is always truthy!
const assumption = item.thing
})
Now, our map
can focus on what it's trying to do, and we've removed the empty values from our pipeline forever.
Hope that helps!