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 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.
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.
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.
- 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:
<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:
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 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] ... ]
.
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 thatconst letters = [‘b’, ‘c’, ‘d’, ‘e’]
array I used as an example before and add ana
at the beginning as follows:array.unshift(‘a’)
. Now the output forconsole.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. 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.
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 thatconst letters = [‘a’, ‘b’, ‘c’, ‘d’, ‘e’]
array and remove thea
at the beginning as follows:array.shift()
. Now the output forconsole.log(letters)
will go from[‘a’, ‘b’, ‘c’, ‘d’, ‘e’]
to[‘b’, ‘c’, ‘d’, ‘e’]
.
Reduce Multidimensional Arrays
The 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 haveconst 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 meansconsole.log(letters2[0]) //output: [‘b’, ‘c’]
, whereasconsole.log(letters2[0][1]) //output: ‘c’
.
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:
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 likeconst letters2 = [['b', 'c'], ['d', 'e']];
we would get an output of 2 fromconsole.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 theletters2
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
.
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( )
.
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
For more on arrays, take a look at MDN web docs in arrays. 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 ❤️ 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.
💻 article.close()
Top comments (0)