Me: The size of an array is set by the variety of its components, proper?
JavaScript: Hmm, probably not . . .
Ah, JavaScript arrays! π
At first look, they appear so easy, only a linear assortment of things, proper? However dig a little bit deeper, and you will find some surprises. Name them simply one other nod to the typically perplexing nature of JavaScript.
On this put up I’ll speak about:
β
what determines the size of an array
β
the distinction between sparse and dense arrays
β
work with sparse arrays
The Case of the Mysterious Array Size
Bear in mind the primary time you thought you’d mastered arrays? Similar. I believed the array size was decided by the variety of outlined components. However alas, JavaScript had different plans.
Sparse Arrays
Let’s create an empty array:
let arr = [] β
Seems to be innocent, proper? Now let’s put a component at index 2:
arr[2] = 5 β
What do you assume arr.size
could be? In case you stated 1, be part of the membership of the fooled!
console.log(arr.size) -> 3 π±
Sure, arr.size
is 3, not 1!
In JavaScript, arr.size = highest index + 1
(plus 1 as a result of we begin indexing at 0).
It is true, this isn’t your on a regular basis array. It is what we name a sparse array. And in the event you’re questioning what a sparse array is, attempt logging the array to the console:
console.log(arr) -> [ <2 empty items>, 5 ] π€
You will discover that there are two empty spots previous the worth 5. These empty spots, referred to as additionally holes, make the array sparse, because it accommodates gaps the place no specific values have been set.
Consider it like a car parking zone the place you resolve to park your automotive in a spot marked #10. This suggests that there are 9 different spots earlier than it. Even when these previous spots are empty, the car parking zone continues to be thought-about to have a capability of 10 spots.
JavaScript arrays function on the identical precept: marking a spot at index 2 means there are two different spots earlier than it (at indices 0 and 1), making the array’s size 3.
Dense Arrays
In distinction, chances are you’ll be extra accustomed to dense arrays, the place each index corresponds to a price, even when it is set to undefined.
let dense = [ "dense", "arrays", "are", "boring"] π
In dense arrays, there are not any gaps; every slot within the array is accounted for, whether or not it is holding a price or is explicitly undefined.
Sparse Array Meets map( )
A Shock
So, you may marvel, what occurs once you run the map() operate on our sparse array?
const newArr = arr.map(x => x + 3)
console.log(newArr) -> [ <2 empty items>, 8 ] π²
Anticipated to see NaN
(ie “Not-a-Quantity”)? So did I. However it seems that map()
simply ignores the empty spots!
Consider a sparse array as a car parking zone divided into two sections: free parking and paid parking. Free parking areas are just like the empty slots in our array. Our parking officer – the map()
operate – ignores them and walks proper previous them.
A Query
A good query to ask: if the empty spots are ignored, why arenβt they simply eradicated from the brand new array? As a result of after our parking officer finishes their rounds, the car parking zone (our array) should stay the identical dimension!
Equally, JavaScript’s map()
methodology will all the time return a brand new array of the identical size as the unique. It does not get rid of empty spots; it retains them as they’re, making certain that the size of the array stays constant.
An Experiment
Now let’s explicitly set the primary factor as undefined
:
arr[0] = undefined
console.log(arr) -> [ undefined, <1 empty item>, 5 ] β
const newArr = arr.map(x => x + 3)
console.log(newArr) -> [ NaN, <1 empty item>, 8 ] π²
Discover how the primary factor of the brand new array is now NaN
. Why?
Once we use map()
on an array in JavaScript, the operate we offer as an argument is known as on every index that has been assigned a price. We all know it ignores the empty spots, nevertheless it does take note of each factor with an assigned worth. Even when that worth is undefined
!
So if we explicitly set a component to undefined
, map()
will certainly invoke the operate on that factor. In our particular instance of arr.map(x => x + 3)
, the operate is making an attempt so as to add 3 to undefined
. In JavaScript, any arithmetic operation involving undefined will output NaN
.
To exhaust our car parking zone analogy: when an array factor is explicitly set to undefined
, it is like a metered however unoccupied spot within the paid parking part. Our parking officer (once more, the map()
operate) walks by and makes word of it. In JavaScript phrases, which means listening to that worth and attempting to work with it.
A Observe
Within the above instance, we acquired fortunate. JavaScript will mechanically convert undefined
to NaN
when it tries to carry out an arithmetic operation. The map()
operate will then proceed to function on the remainder of the weather within the array.
It’s completely different with strings. When map()
encounters undefined
and the operate is attempting to, letβs say, convert it to lowercase, you may run right into a TypeError
as a result of undefined
shouldn’t be a string and doesn’t have a toLowerCase()
methodology. The execution stops at that time.
const array = ['HELLO', 'WORLD', undefined]
const newArray = array.map(factor => factor.toLowerCase()) π«
//TypeError: Can not learn properties of undefined
To make sure your code runs easily, it is important to deal with undefined
values earlier than calling any strategies on them: filter them out earlier than making use of map()
or use a try-catch
block. And naturally, don’t purposefully declare your components as undefined! We did it right here within the title of studying. π
Sparse Array Meets filter()
Should not we simply filter out the empty spots as nicely? In fact! You possibly can filter out empty spots by utilizing the filter()
methodology. Bear in mind how map()
ignores them? Nicely, the empty slots are being handled as undefined
for the aim of filtering!
Letβs take our up to date array and apply filter()
to it. The array has undefined
at first index, adopted by an empty spot
, and worth 5 at index 2.
console.log(newArr) -> [ undefined, <1 empty item>, 5 ]
const filteredNewArr = newArr.filter(x => x !== undefined);
console.log(filteredNewArr) -> [5] β
Okay, however what if, theoretically, you solely wish to take away the holes however hold the undefined
? You are able to do one thing like:
const filteredNewArr = newArr.filter((merchandise, index) =>
arr.hasOwnProperty(index));
console.log(filteredNewArr) -> [ undefined, 5 ] β
On this instance, hasOwnProperty()
checks if the array has an precise worth, together with undefined
, at every index. Due to this fact, it should return true for all indices the place a price exists and false for holes.
To Recap
βοΈ Not all array are dense. Some have holes and we name them sparse.
βοΈ For the aim of discovering the size, we should rely the holes as nicely.
βοΈ The map()
methodology ignores the holes, nevertheless it doesn’t take away them.
βοΈ We are able to take away the holes with the filter()
methodology.
Are We Able to Conclude?
Is a sparse array a factor in real-world purposes? I donβt have a solution but, and promise to replace the put up if and once I do. However then, even when the reply is a powerful no, it doesn’t matter. It will not make these quirky sides of JavaScript arrays any much less fascinating to discover. Lengthy reside quirkiness!
Hold exploring! β΅
Sources
JavaScript: The Definitive Information
seventh Version, by David Flanagan
O’Reilly Media, 2020
Weblog put up initially revealed on August 1, 2023 on corinamurg.dev.
Credit score: Picture by Krzysztof Kotkowicz on Unsplash