Power Javascript Array Initialization

Introduction
Essentially, initializing arrays in javascript can happen in one of four ways:
- With Array literal initialization, such as in
let myArray = [1,2]
- With Array initialization with
undefined
, as innew Array(arrayLength)
- With Array initialization with given elements, as in
new Array(el1,el2)
- With the Array.prototype.of method, as in
// -> [7]
This being known, there eventually comes a time when a learning javascript developer will need to have more control over the initialization of arrays. In the coming sections we will explore common array initialization questions, as well as some solutions for the power javascript developer to-be.
Question: How to initialize an array with the same value?
It’s a recurring question. That is the developer wants to initialize an array, of a given length with the same value in each slots of the array. Obviously, what the developer is asking here is what is the most elegant way to do so. A straightforward way to do so is:
Question: How to initialize an array with sequential numbers?
Here we won’t use Array.prototype.fill
. Instead, the developer will do something like:
Or better yet, the power developer might want to use generators:
[A] Generator is an object returned by a generator function and that conforms to both the iterable protocol and the iterator protocol. […] The iterable protocol allows JavaScript objects to define or customize their iteration behavior, such as what values are looped over in a for..of construct. […] The iterator protocol defines a standard way to produce a sequence of values (either finite or infinite), and potentially a return value when all values have been generated. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols
Such a developer might write:
Here a generator that yield
s numbers between from
and to
is used to construct an array. The fact that generators are iteratable is used with the spread ...
operator to concisely initialize our sequential number array. It's easy to see that we can now also have a fill
function that can fill from an arbitrary number to another, as in:
Question: How to initialize an array with a recurring pattern of numbers?
An quick and dirty way to achieve this is:
This would work with arrays of characters too, as with xs
being something like "Hello".split('')
. As you might have guessed, the power javascript developer will sense that there is room for improvement here. Inspired by Haskell’s cycle
and take
functions, he will probably write:
Here, the power developer might once again think of the ECMAScript 2015 iterator protocol:
The iterator protocol defines a standard way to produce a sequence of values (either finite or infinite), and potentially a return value when all values have been generated.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols
It stipulates that an ECMAScript iterator should have a next()
method that returns a {value:any, done:boolean}
object that contains the value
returned by the iterator and done
that is false
if more values are held by the iterator or true
otherwise. You will note, in the gist above, that the iterator returned by cycle
never sets done
to true
. Meaning, it is never out of values to yield. It just cycles through them. Please keep it in mind, as cycle is an important component of what we will do in the rest the article. Now, using repeatPattern2
will give the same result as repeatPattern
. However, cycle
and take
allow us to do more interesting things, like answering the next question.
How to initialize an array by cycling through a sequence of elements?
That is, if given an array [1,2,3]
and n=4
, how to use it to initialize an array arr, such that arr
is [1,2,3,1]
? Using cycle
and take
, we can write the following function:
This will do the job for any pattern of elements.
Remember the first question? How to initialize an array with the same element? Now, if we change our cycle function so that it accepts a single element with the line let els = Array.isArray(xs) ? xs : [xs]
to have:
We can then have the function same
:
It will return an array of the length specified n
filled in with the specified element el
. As in fiveOnes = same(1,5) // -> [1,1,1,1,1]
or threeEmptyObjects = same({},3) // -> [{},{},{}]
With another small change:
let els = Array.isArray(x) ? x : typeof y === 'undefined' ? [x] : [...filler(x,y)]
,
cycle can be used to provide a powerful and flexible way to initialize an array by cycling through a sequence of numbers:
With those changes, cycle
can now accept:
- a single element to cycle through
- an array of elements to cycle through, or
- two numbers
x,y
in order to generate a sequence of number in the range x..y, from which it will cycle.
The method cycleThroughNumberPattern(x,y,n)
essentially uses the cycle(x,y)
form of cycle
to become a more powerful version of our earlier arrayFillSequential
function.
Improvements on Cycle and Take
The take(n,xs)
function can be made to directly accept arrays and be more robust. First we need to ensure that its xs parameter is not already an iterator object with typeof xs.next === 'undefined'
. If not, we need a method to turn our array into an iterator. Also, we want to stop requesting elements from xs
if xs
contains less elements than n
. Adding these changes, we finally have:
Earlier, we mentioned the iterable protocol. We might want to let our cycle function return an object that is also an iterable. This will allow us to retrieve the sequence our cycle is operating on by using the spread ...
operator. So our modified cycle
function is now:
Bonus Question: How to initialize an array by cycling through a sequence of random numbers?
First the power developer, that you are, needs a way to generate random numbers sequences of arbitrary length. Here is how he does it:
The important part here is our randomFiller
generator that only yields once, but when it does, what is yielded is assured to be an array of length n
containing n
unique random numbers between min
and max
.
Then, all that is left to do is to use take
and cycle
to generate an array cycling through the generated random numbers; for example:
take(15, cycle(randomNumberSequence(1,30,10)))
Conclusion
The cycle
and take
functions, with the support of generators, will help the power javascript developer craft array generation functions that are elegant and powerful. To take what we just spoke about further, the lucid javascript developer will want to use partial application, compose and currying to build even more powerful array generation tools.
The code for this article can be found at https://github.com/kanian/power-js-array-initialization-article
Originally published at https://patricksolutionsarchitect.com on May 21, 2019.