7. Arrays
Table of Contents
Array Basics
Arrays are lists of data (order matters). Values in an array are called elements of the array.
Arrays use square brackets
[]to encapsulate the dataArrays are a type of object
Like strings arrays also...
have indexes starting at
0use bracket notation to access individual elements:
array[index]have a
.lengthproperty that returns the number of elements in the arrayhave many of the same read-only methods like
slice,indexOfandincludes
Unlike strings, arrays are mutable.
const friends = ['bert', 'ernie', 'big bird', 'kermit', 'miss piggy', 'elmo'];
console.log(`typeof on an Array returns ${typeof friends} because Arrays are Objects!`);
console.log(`I have ${friends.length} friends`);
console.log(`My best friend is ${friends[2]}`);
console.log(`My first three friends are ${friends.slice(0, 3)}`)
console.log('But here are all of my friends:');
for (let i = 0; i < friends.length; i += 1) {
// friends[i] holds the "current" friend that we're looking at in this iteration of the loop
console.log(friends[i]);
}Challenge: This function can verify whether or not a given array arr contains a given value value. For each numbered comment below, add a comment explaining the logic of the function!
const hasValue = (arr, value) => {
for (let i = 0; i < arr.length; i++) { // 1.
if (arr[i] === value) { // 2.
return true; // 3.
}
}
return false; // 4.
}
const letters = ['a', 'b', 'c', 'd'];
console.log(hasValue(letters, 'c')); // Prints true
console.log(hasValue(letters, 'e')); // Prints falseArrays Are Mutable
Strings have read-only methods like toUpperCase() and slice() that make a copy of the string but don't change the original string.
const myName = 'ben';
myName.toUpperCase(); // doesn't mutate the string, it makes a copy
myName[0] = "J"; // doesn't even do anything
console.log(myName); // still 'ben'Unlike strings, arrays let you mutate them directly (change their contents without reassignment).
const endLetters = ["x", "y", "z"];
endLetters[1] = "foo";
console.log(endLetters); // ["x", "foo", "z"]We can even modify the .length of an Array to empty them:
endLetters.length = 0; // endLetters now has no values
console.log(endLetters); // []Array Methods for Adding and/or Removing Values
Since arrays are mutable, they not only have read-only methods like slice, indexOf, and includes, they also have methods for adding to and removing from the array.
These methods let you add values to an array
arr.push(value)— adds a given value to the end of an array, increasing the length by 1arr.unshift(value)— adds a given value to the front of an array, increasing the length by 1
These methods let you remove values from an array
arr.pop()— removes a given value from the end of an array, reducing the length by 1arr.shift()— removes a given value from the front of an array, reducing the length by 1
Splice lets you add and remove values to and from an array all at once!
arr.splice(index, qtyToRemove, valuesToAdd)— removes or replaces elements in an array and/or adds new elements to an array
const letters = ['a', 'b', 'c', 'd', 'e'];
letters.push('f'); // adds 'f' to the end of letters
letters.unshift('z'); // adds 'z' to the beginning of letters
console.log(letters); // Prints ['z', 'a', 'b', 'c', 'd', 'e', 'f']
letters.pop(); // removes the last element ('f')
letters.shift(); // removes the first element ('z')
console.log(letters); // Prints ['a', 'b', 'c', 'd', 'e']
letters.splice(2, 0, 'Hi!'); // At index 2, removes no elements and inserts 'Hi!'
console.log(letters); // Prints ['a', 'b', 'Hi!', 'c', 'd', 'e']
letters.splice(2, 1, 'Hey ;)'); // At index 2, replaces 1 element with 'Hey ;)'
console.log(letters); // Prints ['a', 'b', 'Hey ;)', 'c', 'd', 'e']
letters.splice(2, 2, 'Nope', 'Bye!'); // At index 2, replaces 2 elements with 'Nope' and 'Bye!'
console.log(letters); // Prints ['a', 'b', 'Nope', 'Bye!', 'd', 'e']Reference vs. Primitive Values
Arrays and Objects are considered reference types. Let's see why.
How Reference Types (Arrays and Objects) are Stored in Memory
When a variable is created, a chunk of your computer's memory is assigned to hold some data. Primitive values are small enough to be stored in memory as-is. Arrays and objects (a.k.a "reference types") however can grow to be any size and therefore cannot be contained within a single memory slot.
Instead, the data in arrays and objects are stored in an area called the heap and a reference to their heap address is stored in the variable instead.
As a result, we can mutate the contents of an array without reassigning the variable because the variable doesn't hold the array, it holds a reference to the array!
Mutability vs. Reassignment
Consider the code below, what will the value of nums and clone be after the program runs?
const nums = [1,2,3];
const clone = nums;
clone[1] = 20;
console.log(nums); // [1,20,3]
console.log(clone); // [1,20,3]Both nums and clone will hold the values [1,20,3]. Why?
Again, this is because arrays are reference types because:
When we assign
clone = nums, the "value" ofnumsis the reference to the array[1,2,3], not the array itself. So,clonealso holds a reference to the exact same arrayWhen we mutate
clone[1], we are mutating the array referenced byclonewhich is the same array referenced bynums. So, bothnumsandclonereturn the mutated array.
The same thing happens when we invoke a function and provide a reference type as an argument:
const emptyTheArray = (arr) => {
// 3. Upon receiving the reference, this function modifies array
arr.length = 0;
}
// 1. The array below is created in the heap and the "reference" to its location is stored in letters
const letters = ['a', 'b', 'c'];
// 2. When we invoke emptyTheArray, we assign the "reference" to the function parameter "arr"
emptyTheArray(letters);
// 4. The array referenced by letters has been modified by the function!
console.log(letters); // Prints []It is impossible for this behavior to occur when dealing with primitive values like strings, numbers, and booleans because they are immutable.
let x = 10;
let y = x;
y++; // shorthand for y = y + 1In this example, even though it looks like we're mutating the value y, we are NOT. We're reassigning y to store a completely different value (11).
Impure and Pure Functions
Functions are considered impure functions if they:
Produce different outputs when given the same inputs
Produce side effects (like mutating incoming values)
The functions below are impure functions:
// This function can't return the same output each time it is invoked.
const rollDie = () => {
return Math.ceil(Math.random() * 6);
}
console.log(rollDie()); // ???
console.log(rollDie()); // ???
let count = 0;
// This function has the side effect of modifying the count variable which is outside of its scope
const incrementBy = (x) => {
count += x;
return count;
};
console.log(incrementBy(2)); // 2
console.log(incrementBy(2)); // 4
console.log(incrementBy(2)); // 6
console.log(count); // 6
// This function mutates the incoming arr value
const emptyTheArray = (arr) => {
arr.length = 0;
}
const letters = ['a', 'b', 'c'];
emptyTheArray(letters):
console.log(letters); // Prints []Making Copies of Arrays to Make Pure Functions with the Spread Syntax
Functions that accept Arrays and modify them are impure. To make a function that modifies an Array pure, we need to make a copy of it first. The most effective way to do this is using the spread syntax: [...arr]
const extend = (arr, value) => {
const newArr = [...arr, value];
return newArr;
}
const letters = ['a', 'b', 'c'];
const moreLetters = extend(letters, 'd');
console.log(letters); // Prints ['a', 'b', 'c']
console.log(moreLetters); // Prints ['a', 'b', 'c', 'd'];Copying Array Challenge
Make this impure array function pure!
const shorten = (arr) => {
arr.pop();
return arr;
}Advanced Array Syntax
2D Arrays
An Array can contain other Arrays! When the inner arrays contain values, we call this a "2-Dimensional (2D)" Array or a "matrix".
The matrix below has 2 columns and 3 rows:
const coordinates = [
[30, 90],
[40, 74],
[34, 118],
[42, 88],
[39, 77]
];
const newOrleans = coordinates[0];
const newOrleansLat = coordinates[0][0];
const newOrleanLong = coordinates[0][1];
console.log(newOrleans);
console.log(newOrleansLat);
console.log(newOrleanLong);When accessing a 2D array, the first index references the "row" and the second index references the "column"
Destructuring Assignment and Rest Operator
The destructuring assignment syntax is a JavaScript expression that makes it possible to "unpack" values from arrays (or properties from objects) into distinct variables.
The syntax uses [] on the left side of an assignment operator = to define which values to unpack from the Array on the right side (the "source variable");
const coordinates = [
[30, 90],
[40, 74],
[34, 118],
[42, 88],
[39, 77]
];
// Unpack the first three arrays from coordinates (we are choosing to ignore the rest)
const [newOrleans, newYork, losAngeles] = coordinates;
console.log(newYork);
// Unpack the two values from the newYork array.
const [newYorkLat, newYorkLong] = newYork;
console.log(newYorkLat);
console.log(newYorkLong);In the example above, we only unpacked the first 3 values of coordinates.
If we want to, we can use the "rest" operator (...rest) to store the remaining unpacked values in a variable:
const [, , ...lesserCities] = coordinates;
console.log(lesserCities);
/*
[
[34, 118],
[42, 88],
[39, 77]
];
*/Last updated