Performing repetitive calls using an ad-hoc array
Tags: javascript, webdev, programming
Published: 2023-01-17 16:00:00

One of the most common uses for the common JS `for` loop must be to traverse the elements of an array. It might be to transform each element, filter a sub-set or convert the entire array into an entirely new data structure. This is probably the reason why the [Array prototype](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array) acquired some super powers in ECMAScript 6 (ES 2015) in the form of some additional methods ([map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map), [filter](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter), [reduce](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce), etc.) These methods make the code simpler to write (largely), understand and, being implemented inside the JS engine, has the potential to offer better performance.

Most of the new methods follow a common pattern in that they take a call-back function that is applied to each item in the array in turn. The majority of the methods return a brand new array (or another object) as the output. The call-back functions are typically one of three types:
* Transform: Converts the input item in to a new item
* Predicate: Applies some logic on the input item and returns a Boolean value as the result
* Reducer: Takes in two parameters, the second being the array item, the first being a 'running total' and produces an updated 'running total'.

__Two additional points:__
1. I used the term 'running total' to describe the first parameter of the reducer but that is not to say it has to be a numeric value. It could be another array, an object or any value that gets passed from one cycle to the next.
2. The call-backs actually take in more that 1 argument (or 2 arguments in the case of `reduce`). In addition to each item from the array it also takes the index of the item and the array itself.

But how can you test the call-backs without creating an array of test data. Here is where the `adhocArray` function comes in useful, although I can also imagine scenarios where the function could be used in operational code.

## Using an ad-hoc Array
As explained above, the majority of the time the call-backs need to operate on the items of an array. We can exercise the call-backs directly but ideally we still need a range of input values/objects.

The primary use case for the ad-hoc array is to exercise Pure functions, which are highly reusable, usually easy to test but often require additional inputs to make up for a lack of context. Pure functions operate entirely on their parameters and nothing outside of that including not changing the value of arguments supplied by reference. They only output via the return value which is deterministic and predictable from the input.

### Implementation
I have experimented with a number of varieties of functions but the following is my preferred implementation to date. It works by first defining an Array of the required size but this only allocates space. The initial Array is expanding it out into another Array, which creates a populated Array. Finally the transform call-back function (default or supplied) is used to convert the index of each element of the array into a new array.

```javascript
function adhocArray(length = 1, transform = _ => _) {
  return [...Array(length).keys()].map(transform);
}
```

An arrow function could easily be used in place of a conventional function but this limits where the function can be declared. Through JS hoisting conventional functions can be declared at the bottom of a script and used before the declaration. This not true of arrow functions that have to be declared before they can be used.

### Usage
The `adhocArray` function enables us to execute a function a set number of times. We have the option of using the content of the array that has been especially created for the purpose. Now lets investigate its behaviour through a few experiments.

#### Default execution
```javascript
console.log(adhocArray(6));  // [ 0, 1, 2, 3, 4, 5 ]
```
At first sight this might appear to be no different to creating an Array by just calling `Array(6)` but that would create an array of 6 empty elements. Using `adhocArray` we get back an array  repopulated, by default with integers incrementing from zero.

#### Basic transform
```javascript
const evenValues =  i => (i + 1) * 2;
console.log(adhocArray(6, evenValues));  // [ 2, 4, 6, 8, 10, 12 ]
```
We can customise the array population using a supplied transform call-back that converts the default integer into whatever is required.

#### Basic transform with post processing (reduce-based sum)
```javascript
const evenValues =  i => (i + 1) * 2;
const sum = (total, inc) => total + inc;
console.log(adhocArray(6, evenValues).reduce(sum));  // 42
```
The array output can the be used for a variety of purposes but its original use case was to enable execution of a (call-back) function a set number of times; to render dummy data in a prototype screen for instance. In the above example I have used it to exercise an experimental reduce-based sum reducer.

#### Populating a 2-dimensional array
```javascript
const multiplier = (_, __) => (_ + 1) * (__ + 1);
const timesTable = adhocArray(12, _ =>
  adhocArray(12, __ => multiplier(_, __))
);
console.table(timesTable);
```
Combining `adhocArray` can also produce a 2-dimensional data set. In this example I have used it to generate a 12x12 times table.

![Times table otput](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mmy7vkx2bfem7wlqln5w.png)

## Bonus transforms
When creating a form that requires a day-of-week or month-of-year input it is not unusual to encounter a variable such as;
```javascript
const DAYS_OF_WEEK = [
  'Sunday',
  'Monday',
  'Tuesday',
  'Wednesday',
  'Thursday',
  'Friday',
  'Saturday'
];
```
But what if you need the list to be in another or multiple languages. We can use the JavaScript [Intl namespace](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl) using the following utility functions to generate day and month names in long or short forms, in a variety of languages (defaulted to British English).

```javascript
const shortDay = (day, lang = 'en-GB') =>
  new Date(0, 0, day).toLocaleString(lang, { weekday: 'short' });

const longDay = (day, lang = 'en-GB') =>
  new Date(0, 0, day).toLocaleString(lang, { weekday: 'long' });

const shortMonth = (mon, lang = 'en-GB') =>
  new Date(0, mon, 1).toLocaleString(lang, { month: 'short' });

const longMonth = (mon, lang = 'en-GB') =>
  new Date(0, mon, 1).toLocaleString(lang, { month: 'long' });
```

Here is a demonstration:

```javascript
console.log(adhocArray(7, shortDay));
console.log(adhocArray(12, shortMonth));

/* Output - Default short day and month in GB English
[
  'Sun', 'Mon',
  'Tue', 'Wed',
  'Thu', 'Fri',
  'Sat'
]
[
  'Jan', 'Feb', 'Mar',
  'Apr', 'May', 'Jun',
  'Jul', 'Aug', 'Sept',
  'Oct', 'Nov', 'Dec'
]
*/

console.log(adhocArray(7, day => longDay(day, 'ga-IE')));

/* Output - Gaelic days of the week (in long form)
[
  'Dé Domhnaigh',
  'Dé Luain',
  'Dé Máirt',
  'Dé Céadaoin',
  'Déardaoin',
  'Dé hAoine',
  'Dé Sathairn'
]
*/

console.log(adhocArray(12, mon => longMonth(mon, 'fr-FR')));

/* Output - French months in long form
[
  'janvier',   'février',
  'mars',      'avril',
  'mai',       'juin',
  'juillet',   'août',
  'septembre', 'octobre',
  'novembre',  'décembre'
]
*/
```

## To Close
I am not convinced the `adhocArray` has any real utility but I found it a useful and interesting exercise all the same. And besides it saves me another reason for writing boring `for` loops.