A Look into Processing MultiDimensional Arrays Using Javascript
Tags: javascript, challenge, tutorial, wecoded
Published: 2020-07-16 21:00:31

_Understanding multidimensional arrays by solving a simple two-dimensional **Harry Potter Challenge** using Vanilla Javascript._

# The Challenge 

The Hogwarts Express has added several new stops on its journey from Platform 9 ¾ in London’s King’s Cross Station to Hogsmeade Station in Hogwarts School of Witchcraft and Wizardry. Dumbledore needs a management tool with which a crew member can input the numbers of passengers boarding and disembarking at each station. With these numbers, this tool will automatically calculate the number of passengers on board the train.  

## Arrays

According to MDN web docs, [arrays](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array) are “list-like objects whose prototype has methods to perform traversal and mutation operations”. 

There is more than one way to create an array. You can use the array constructor `const array1 = new Array(“item1”, “item2”)`, but it is recommended for both readability and speed to use the array literal method. Below we will use the literal method to create a one-dimension array with the names of the train stations from London to Hogwarts. 

```javascript
const stations = [
   "King's Cross",
   'Waverly',
   'Weasley',
   'Cardiff Central',
   'Hogsmeade',
];
```

A two-dimensional array is just a one-dimensional array inside another array. For each station, a pair of elements will be added in a `[ [b1, d1], [b2, d2] … ]` fashion, where `b` represents the number of passengers that have boarded the train and `d` represents the number of passengers that have disembarked the train. For now, we will just create an empty array that will be populated with new information submitted by the crew member in charge of the train’s passenger capacity. 

```javascript
const passengerLog = [ ];
```

## Accessing HTML Elements

What is important for us to know about the HTML are the `class names` of the elements inside the form. 

>Note: There are different ways to name your classes. In this project, I used the BEM naming convention. For more on this, you can do some research on naming conventions [here](https://www.freecodecamp.org/news/css-naming-conventions-that-will-save-you-hours-of-debugging-35cea737d849/).

* Station name’s display area: `.station__name`
* Passengers boarding input box: `.passengers__in`
* Passengers disembarking input box: `.passengers__out`
* Submit button: `.button__add-passengers`
* Passengers onboard display area: `.passengers__total`

HTML:
```html
<form>
      <table>
        <tr>
          <th colspan="2">STATION</th>
        </tr>
        <tr>
          <td colspan="2" class="station__name"></td>
        </tr>
        <tr>
          <th>IN</th>
          <th>OUT</th>
        </tr>
        <tr>
          <td class="input__in-out">
            <input
                   class="passengers__in"
                   type="number"
                   min="0"
                   />
          </td>
          <td class="input__in-out">
            <input
                   class="passengers__out"
                   type="number"
                   min="0"
                   value="0"
                   />
          </td>
        </tr>
        <tr>
          <td colspan="2">
            <button type="submit" class="button__add-passengers">
              Submit
            </button>
          </td>
        </tr>
      </table>
      <br />
      <p class="passengers__total"></p>
    </form>
```

Next, we will be using the `querySelector` method and using the class selectors mentioned before in order to return the elements we need. The `form` itself has no class name so you can use the same method, but omitting the `.` at the beginning of the selector name. 

JavaScript:
```javascript
const stationName = document.querySelector('.station__name');
const passengersIn = document.querySelector('.passengers__in');
const passengersOut = document.querySelector('.passengers__out');
const buttonAddPassengers = document.querySelector('.button__add-passengers');
const passengersTotal = document.querySelector('.passengers__total');
const form = document.querySelector('form');
```

## Adding Elements to Multidimensional Arrays

The [push](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push) method will add elements to the end of an array. If we have an array `const letters = [‘b’, ‘c’, ‘d’]` and want to add a fourth letter we just need to use the `array.push(‘e’)` method to add that letter. This means the output for `console.log(letters)` will go from `[‘b’, ‘c’, ‘d’]` to `[‘b’, ‘c’, ‘d’, ‘e’]`. 

As mentioned before, multidimensional arrays are just one-dimensional arrays inside other one-dimensional arrays so the same method can be used to add an array into an array. We already created an array called `passengerLog`. Before leaving each station, the crew member in charge will use the form in order to submit two numbers. The number of passengers that boarded the train and the number that disembarked at said station. 

These two numbers will need to be added to the `passengerLog` array using the `push` method `passengerLog.push([passengersInValue, passengersOutValue]);`. When adding an element in the form of an array you will be creating a two-dimensional array that will be constructed as follows: `[ [passengersInStationOne, passengersOutStationOne], [passengersInStationTwo, passengersOutStationTwo] ... ]`.

```javascript
const passengersIn = document.querySelector('.passengers__in');  // already established 
const passengersOut = document.querySelector('.passengers__out');  // already established 

const passengerLog = []; // already established 

function addToPassengerLog() {
   let passengersInValue = passengersIn.value;
   let passengersOutValue = passengersOut.value;

  // Replace empty input boxes with 0 before adding to log
  // Add last entry to the passenger log
   if (passengersInValue && passengersOutValue) {
      return passengerLog.push([passengersInValue, passengersOutValue]);
   } else if (passengersInValue && !passengersOutValue) {
      return passengerLog.push([passengersInValue, 0]);
   } else if (!passengersInValue && passengersOutValue) {
      return passengerLog.push([0, passengersOutValue]);
   } else {
      return passengerLog.push([0, 0]);
   }
}
```

I’ve gone a step further for this project and added an `if...else statement` to check for empty input logs and change them to zero before adding the input values into the main array. The `if...else statements` all have a logical `&&` operator which will return `true` if both statements on either side of the `&&` operator are true. 

If `(passengersInValue && passengersOutValue)`, means that `passengersInValue` and `passengersOutValue` exist. So, as we state below it, both values will be added to the `passengerLog` array. However, if `(passengersInValue && !passengersOutValue)` this means that the `passengersInValue` exists, but the `passengersOutValue` does not (that is what the `!` not operator is for). That input box has been left empty on the form. Hence, we add the `passengersInValue` value and a zero, as stated below that `else if statement`.

> Extra: If we wished to add an element (or elements) to the beginning of an array, we could use the `unshift` method. We can take that `const letters = [‘b’, ‘c’, ‘d’, ‘e’]` array I used as an example before and add an `a` at the beginning as follows: `array.unshift(‘a’)`. Now the output for `console.log(letters)` will go from `[‘b’, ‘c’, ‘d’, ‘e’]` to `[‘a’, ‘b’, ‘c’, ‘d’, ‘e’]`. 

## Removing Elements from Multidimensional Arrays

The method used to remove the last element from an array is called [pop](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/pop). This will be an easy one. As we push elements into the array we get a two-dimensional array that looks like this: `[ [passengersInStationOne, passengersOutStationOne], [passengersInStationTwo, passengersOutStationTwo] ]`. When we use the `pop` method, it will remove not just `passengersOutStationTwo`, but the whole `[passengersInStationTwo, passengersOutStationTwo]` array as this whole array is considered an element of the `passengerLog` array. 

```javascript
const passengerLog = []; // already established 

function removeFromPassengerLog() {
  // Remove last entry from passenger log
  return passengerLog.pop();
}
```

> Extra: If we wished to remove an element from the beginning of an array, we could use the `shift` method. We can take that `const letters = [‘a’, ‘b’, ‘c’, ‘d’, ‘e’]` array and remove the `a` at the beginning as follows: `array.shift()`. Now the output for `console.log(letters)` will go from `[‘a’, ‘b’, ‘c’, ‘d’, ‘e’]` to `[‘b’, ‘c’, ‘d’, ‘e’]`. 

## Reduce Multidimensional Arrays

The [reduce](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce) method will execute a function determined by you on each element. After it is done it will return a single output value. 

Reduce can take in four arguments. An accumulator(acc), current value (cur), current index (idx), and source array (src) with a syntax `array.reduce(function(acc, cur, idx, src), initialValue)`. 

For this example, we will only use the first two arguments (which are the only two that are required). Applying `reduce` to the `passengerLog` array means that for each `[passengersInStationX, passengersOutStationX]` entry we will execute the function provided. The results will be stored inside the accumulator (acc) before going to the next entry, executing the function on that next set of values, and adding the result to the accumulator. 

> Side Note: Arrays are zero-based. To access an item in an array you can use the index position as follows: `const letters = [‘b’, ‘c’, ‘d’, ‘e’]` `console.log(letters[0]) //output: ‘b’` `console.log(letters[1]) //output: ‘c’`. As for multidimensional arrays, if we have `const letters2 = [ [‘b’, ‘c’], [‘d’, ‘e’] ]` and we want to access the letter ‘c’ we would need to use the index of the first element `[‘b’, ‘c’]` and then the index of the letter ‘c’ inside that array. That means `console.log(letters2[0]) //output: [‘b’, ‘c’]`, whereas `console.log(letters2[0][1]) //output: ‘c’`. 

```javascript
function calculatePassengersTotal() {
  // Calculate number of passengers onboard.
   return passengerLog.reduce((acc, cur) => {
      return (acc += cur[0] - cur[1]);
   }, 0);
}
``` 

Another way of doing the same thing would be as follows:

```javascript
function calculatePassengersTotal() {
  // Calculate number of passengers onboard.
  return passengerLog.reduce((onboard, [boarding, disembarking]) => {
      return (onboard += boarding - disembarking);
   }, 0);
}
```

## Next Station! 

The names of the stations are given to us so the management tool will automatically let the crew member in charge know which station they are at.  

We will declare a couple of variables first. One for the index of the station, and since arrays are zero-based we will assign the number zero to it. The second variable will store the length of the array containing the names of the stations. 

> Side Note: The `length` property returns the number of elements in an array. Arrays may be zero-based, but for this property we start counting from one. For a multidimensional array like `const letters2 = [['b', 'c'], ['d', 'e']];` we would get an output of 2 from `console.log(letters.length)` and not 1 (as if we started counting from zero) nor 4 (as if we were counting each letter as a separate element of the `letters2` array). 

The basic form of this `nextStation` function is `stationName.innerHTML = stations[stationIndex]` so as to change the HTML content of the `stationName` element to the station on the index `stationIndex`. Since we assigned that to zero the first station will be `King's Cross`. 

```javascript
const stations = [     // already established 
   "King's Cross",
   'Waverly',
   'Weasley',
   'Cardiff Central',
   'Hogsmeade',
];   

let stationIndex = 0;
let stationLength = stations.length;   // output: 5

function nextStation(stationIndex) {
  // Display name of station. 
  // For the last two stations the text on the button will change.
  // On the last station the button will be disabled.
   if (stationIndex == stationLength - 2) {
      buttonAddPassengers.textContent = 'Last Station Coming Up';
   } else if (stationIndex == stationLength - 1) {
      buttonAddPassengers.textContent = 'End of Journey';
      buttonAddPassengers.disabled = true;
   } 
   return (stationName.innerHTML = stations[stationIndex]);
}

nextStation(stationIndex);
```

I have added an `if...else statement` to change the text content of the submit button for when the train is approaching the destination and to change it again when the journey has ended (disabling the button at the same time).  

## Submit Passenger Log

The main function that will be executed when we hit that submit button on the form is the one below. On it we will first call the `addToPassengerLog()` function to add (or `push`) the inputs logged by the crew member. Then we declare a variable and assign the results from the `calculatePassengersTotal()` function, which will give us the number of passengers on board. 

If the total number of passengers is zero or more then we display the number in the assigned space provided using `innerHTML` on that element. Next, we clear the input boxes using `form.reset();`. Finally, we increase the `stationIndex` by one and, with that, display the next station using the `nextStation(stationIndex)` function.

If the total number of passengers is less than zero, then that means there was a mistake since we can’t have a negative number of passengers on board. To give the crew member a chance to correct that mistake we will remove that last entry from the log using the `removeFromPassengerLog()` function, clear the input boxes and send a message about the mistake for which the crew member will have to confirm they have read using `window.confirm( )`.

```javascript
const stationName = document.querySelector('.station__name');  // already established 
const passengersIn = document.querySelector('.passengers__in');  // already established 
const passengersOut = document.querySelector('.passengers__out');  // already established 
const passengersTotal = document.querySelector('.passengers__total');  // already established 
const form = document.querySelector('form');  // already established 

function submitPassengerLog(e) {
   e.preventDefault();
   addToPassengerLog();
   let totalPassengers = calculatePassengersTotal();
  
  if ( totalPassengers >= 0 ) {
    // Display the total number of passengers on board.
    passengersTotal.innerHTML = `Passengers onboard: ${totalPassengers}`
   }
   // Clear input boxes.
   form.reset();
   // Display next station
   stationIndex++;
   nextStation(stationIndex);
    
  } else {
    
    removeFromPassengerLog();
    form.reset();
    window.confirm("Check how many witches and wizards are on the train./nYou can't have more of them leaving the train than are onboard.");
  }
   
}

form.addEventListener('submit', submitPassengerLog);
```

## Demo

{% codepen https://codepen.io/santiagocodes/pen/oNbdQma %}

For more on arrays, take a look at MDN web docs in [arrays](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array). I hope that with what we have seen here together you now have a better understanding of multidimensional arrays and how to work with them on more complicated projects. 

If you liked what you read hit that :heart: on the left or wherever it is. If you really liked it don’t forget to share it with the community by hitting that dot-dot-dot icon near the heart. 

:computer: **article.close()**

### Resources

* [Original Challenge](https://didacticode.com/reto-harry-potter/)
* [Naming Conventions](https://www.freecodecamp.org/news/css-naming-conventions-that-will-save-you-hours-of-debugging-35cea737d849/)
* [Arrays](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)
* [Push Method](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push)
* [Pop Method](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/pop)
* [Reduce Method](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce)