JavaScript 2: Loops, Arrays, and Objects#
In this lecture we start to dive into some more functionality of JavaScript. We will learn how to loop through arrays and objects and how to access and edit specific values nested within them. All of this is useful for storing and retrieving data in an organized way, a skill that will be essential to being a successful developer.
To download the demo code for this lecture:
$ dmget wb-js-2 --demo
The topics covered in this lesson will act as the foundation for most of the more advanced subjects we will cover. Please make sure you are comfortable with all of these ideas and make sure to ask for help if you’re not sure about something.
Loops#
Loops#
Loops are very useful in programming. Say we wanted to print out all even numbers between 1 and 10. We could do it this way, but it would be very repetitive:
let n = 2;
console.log(n);
n += 2;
console.log(n);
// keep going until you get to 10 ...
For Loops#
Instead we can use a for loop like this:
for (let n = 2; n <= 10; n += 2) {
console.log(n);
}
There are three parts to a for
loop:
Start condition. Start with
n = 2
.Stop condition. Keep going as long as
n <= 10
.Increment. After each time through the loop, increment
n
by 2.
This table traces the value of n
and how it changes during the loop:
Iteration no. |
|
Next value |
---|---|---|
0 |
\(n = 2\) |
\(n + 2 = 4\) |
1 |
\(n = 4\) |
\(n + 2 = 6\) |
2 |
\(n = 6\) |
\(n + 2 = 8\) |
3 |
\(n = 8\) |
\(n + 2 = 10\) |
4 |
\(n = 10\) |
\(n + 2 = 12\) |
After iteration no. 4, the next value for \(n\) is \(12\) which fails the
condition n <= 10
, so the loop stops. As a result, the loop will output 2, 4, 6, 8, and 10.
for
loops can also count down:
for (let n = 10; n > 0; n -= 1) {
console.log(n);
}
console.log("Blast off!");
These type of
for
loops are sometimes called “generic”for
loops.
Arrays#
Accessing/reassigning values in an array#
Arrays are organized by index. The first item in an array always has index 0:
To access or reassign the item at a certain index of an array, use square brackets:
const fruits = ['avocado', 'berry', 'cherry']; let idx = 2; console.log(fruits[idx]); // 'cherry' fruits[0] = 'apple'; // reassigns first item console.log(fruits); // ['apple', 'berry', 'cherry']
Modifying a const
array#
Even if an array is a
const
, we can still modify the array by changing individual items in it.const fruits = ['avocado', 'berry', 'cherry']; fruits[0] = 'apple'; // OK! array is modified, not reassigned.
However, it won’t work to reassign the entire array:
const fruits = ['avocado', 'berry', 'cherry']; fruits = ['peach', 'pineapple', 'pomegranate']; // error!
Objects can also be modified. But primitive types (e.g. numbers, strings) cannot.
So a
const
variable with a primitive type cannot be changed at all.
Array length#
All arrays have a length
property by default.
Since arrays are zero-indexed, the index of the last item in the array is actually the length minus 1.
// Get the length of an array
// fruits = ['apple', 'berry', 'cherry'];
console.log(fruits.length); // 3
// Get the last element of an array
console.log(fruits[fruits.length - 1]); // cherry
Array iteration#
The for...of
statement is a special for
loop which allows you to iterate through each element in an array.
for (const fruit of fruits) {
console.log(fruit); // apple, berry, cherry
}
We can also use a generic for
loop to iterate through an array by its indices:
for (let i = 0; i < fruits.length; i += 1) {
console.log(fruits[i]); // apple, berry, cherry
}
Array methods#
Arrays have methods to easily modify their contents.
push
andpop
: add or remove items from the end of an array// .push() adds an element to the end of an array fruits.push('dragonfruit'); console.log(fruits); // ['apple', 'berry', 'cherry', 'dragonfruit'] // .pop() removes the last element from an array const lastFruit = fruits.pop(); console.log(lastFruit); // dragonfruit console.log(fruits); // ['apple', 'berry', 'cherry']
unshift
andshift
: add or remove items from the beginning of an array// .unshift() adds an element to the beginning of an array fruits.unshift('watermelon'); console.log(fruits); // ['watermelon', 'apple', 'berry', 'cherry'] // .shift() removes the first element from an array const firstFruit = fruits.shift(); console.log(firstFruit); // watermelon console.log(fruits); // ['apple', 'berry', 'cherry']
slice
: copy the array or get a sub-array (does NOT modify the array)// .slice() returns a copy of an array const fruits2 = fruits.slice(); console.log(fruits2); // ['apple', 'berry', 'cherry'] // .slice() can also take a start and end index const fruitsSubset = fruits.slice(1, 3); console.log(fruitsSubset); // ['berry', 'cherry']
splice
: removes items from an array, and optionally adds new items// .splice() removes elements from an array and optionally adds new elements fruits.splice(1, 1); // remove 1 element at index 1 console.log(fruits); // ['apple', 'cherry'] fruits.splice(1, 0, 'berry'); // remove 0 elements at index 1 and add 'berry' console.log(fruits); // ['apple', 'berry', 'cherry']
Some of this also works for strings#
Indexing,
length
, andfor...of
also work for strings.However, the array methods do not.
You also cannot reassign individual characters in a string.
Objects#
Accessing values with the dot operator#
Previously, you learned how to use the dot operator (.
) to access the values of
properties on an object.
const fruitsInventory = {
apple: 2,
berry: 4,
cherry: 6,
};
console.log(fruitsInventory.apple); // 2
You can also access the values of properties with bracket notation.
Accessing values with bracket notation#
The most common use-case for bracket notation is when the key name is stored in a variable:
const fruitName = 'apple'; console.log(fruitsInventory[fruitName]); // 2
Note that
fruitsInventory.fruitName
will NOT work here!This will look for a key called
'fruitName'
— it won’t treatfruitName
as a variable.
You must also use bracket notation instead of dot notation if the key name contains a space:
const weirdFruits = {
'star apple': 2,
kumquat: 4,
loganberry: 6,
};
console.log(weirdFruits['star apple']); // 2
Adding/updating values#
The same syntax is used to add/update values in an object.
If the key exists, the value is updated.
const fruitsInventory = {apple: 2, berry: 4, cherry: 6}; fruitsInventory.apple = 3; console.log(fruitsInventory); // { apple: 3, berry: 4, cherry: 6}
If the key doesn’t exist, a new key/value pair is added.
fruitsInventory.dragonfruit = 8; console.log(fruitsInventory); // { apple: 3, berry: 4, cherry: 6, dragonfruit: 8 }
You can also add/update objects with bracket notation:
const weirdFruits = {'star apple': 2, kumquat: 4, loganberry: 6};
fruitsInventory['kumquat'] = 5;
// value of 'kumquat' updated to 5
weirdFruits['prickly pear'] = 7;
console.log(weirdFruits);
// {'star apple': 2, kumquat: 5, loganberry: 6, 'prickly pear': 7}
Object syntax flowchart#
Assigning a value
Accessing a value
The delete
operator#
The delete
operator removes a key-value pair from an object.
const fruitsInventory = { apple: 3, berry: 4, cherry: 6, dragonfruit: 8 };
delete fruitsInventory.dragonfruit;
console.log(fruitsInventory); // { apple: 3, berry: 4, cherry: 6}
Looping through objects#
You can loop through all the keys of an object with a for...in
loop:
for (const fruit in fruitsInventory) {
console.log(fruit, fruitsInventory[fruit]);
}
Output:
apple 3
berry 4
cherry 6
for...of
does not work with objects!
Values, entries, and keys#
You can get just the values of an object with
Object.values()
:console.log(Object.values(fruitsInventory)); // [3, 4, 6]
Object.entries()
gets you both the key and the value:for (const [fruit, num] of Object.entries(fruitsInventory)) { console.log(fruit, num); }
There is also
Object.keys()
which gets you an array of keys.
Caution: Don’t use for...in
with arrays#
const fruits = ['apple', 'berry', 'cherry'];
for (const f in fruits) {
console.log(f);
}
// Output:
// 0
// 1
// 2
If you try to loop through an array with for...in
, you’ll actually get all the indices.
Nested data structures#
Nested data structures#
Arrays can be nested inside other arrays:
const tictactoe = [
['X', 'O', 'X'],
['O', 'X', 'O'],
['?', 'O', 'X'],
];
// Get the value in the 3rd row, 1st column ('?')
console.log(tictactoe[2][0]);
// Print out all values
for (const row of tictactoe) {
for (const cell of row) {
console.log(cell);
}
}
Arrays and objects can be nested inside each other:
const fruits = [
{
name: 'apple',
genus: 'Malus',
colors: ['red', 'yellow', 'green'],
},
{
name: 'berry',
genus: 'Vaccinium',
colors: ['red', 'blue'],
},
{
name: 'cherry',
genus: 'Prunus',
colors: ['red'],
},
];
console.log(type(fruits)); // Array
console.log(type(fruits[0])); // Object
console.log(fruits[0].colors); // ['red', 'yellow', 'green']
console.log(type(fruits[0].colors[1])); // 'yellow'
The ternary expression#
The ternary expression#
The ternary expression is a commonly used syntactic shortcut for if...else
statements.
Instead of this:
const isRaining = true;
let outerwear;
if (isRaining) {
outerwear = 'raincoat';
} else {
outerwear = 'cardigan';
}
We can write this, using a ternary expression:
const isRaining = true;
const outerwear = isRaining ? 'raincoat' : 'cardigan';
console.log(outerwear); // raincoat
The syntax for a ternary looks like this:
condition ? what to do if true : what to do if false
However, it’s considered a bad practice to use ternaries of an assignment expression because they can make your code less readable and maintainable.
When to not use ternaries
For example, this is perfectly valid code:
const n = 1;
n < 100 ? console.log('small number') : console.log('big number');
…but you should probably refactor it into a normal if...else
. If the logic in your
code ever changes, it’s a lot easier to add conditions to an if...else
than a ternary
expression.