Slides
Arrays are Objects
Surprise!
Every JavaScript array is also a JavaScript object
That means that arrays have properties and methods like any other object.
Examples:
-
array.length
is a read-only property that always contains the number of elements in the array -
array.reverse()
is a method that reverses the ordering of all elements in the array
See MDN: Array to see a lot more array methods
Iteration Methods
Every JavaScript array has a few very handy methods that let you apply a function to its contents.
method | description | returns |
---|---|---|
forEach |
do something to each item | undefined |
find |
find the first item that matches | one matching item (or undefined if no match) |
filter |
accept or reject each item | a new collection, possibly smaller |
map |
change each item into a new item | a new collection of the same size |
reduce |
scan the entire collection and "reduce" it to... | ...a single result, e.g. a total |
- We call this group of methods "iteration methods"
- There are about a dozen built-in iteration methods, plus lots more added by libraries like lodash.
forEach
forEach
works a lot like for..of
, but using a callback function
Given this array of names...
let names = ['Alice', 'Bob', 'Carol', 'Charlie', 'David']
this code... | and this code... |
---|---|
|
|
both print the same thing:
ALICE
BOB
CAROL
CHARLIE
DAVID
Find
to find the first item that matches the condition...
let names = ['Alice', 'Bob', 'Carol', 'Charlie', 'David'];
let beginsWithC = function(word) {
return word.charAt(0).toUpperCase() === 'C';
};
let cName = names.find(beginsWithC) //=> 'Carol'
Note that:
- the
beginsWithC
function returnstrue
orfalse
- the
find
method returns an item (from the array)
Find Inline
For conciseness, people often define the filter function inline, like this:
names.find((word) => word.charAt(0).toUpperCase() === 'C')
Q: Is this more or less clear than the previous slide?
Lab: Find a Berry
Given the following array:
let fruits = ['Apple', 'Blueberry', 'Cherry', 'Date', 'Elderberry']
write some code that uses find
to return the first item that ends with the string 'berry'
(in this case, 'Blueberry'
)
Filter
the filter
iteration method returns all matching values, in a new array
let names = ['Alice', 'Bob', 'Charlie', 'Carol', 'David'];
let beginsWithC = function(word) {
return word.charAt(0).toUpperCase() === 'C';
}
let cNames = names.filter(beginsWithC) //=> [ 'Charlie', 'Carol' ]
Lab: Find all Berries
Given the following array:
let fruits = ['Apple', 'Blueberry', 'Cherry', 'Date', 'Elderberry']
Now go find your code from the previous lab ("Find a Berry")
and change it to use filter
to return a new array
containing all the fruits that end with the string 'berry'
Hint: all you need to do is change
find
tofilter
-- the matching function itself is the same.
Map
The map
iteration method returns a new array whose elements correspond to the elements of the original array.
let names = ['Alice', 'Bob', 'Charlie', 'Carol', 'David'];
let upper = function(word) {
return word.toUpperCase();
}
let bigNames = names.map(upper) //=> [ 'ALICE', 'BOB', 'CHARLIE', 'CAROL', 'DAVID' ]
It's called "map" because in a mathematical sense, it defines a mapping from one collection to another.
from | to |
---|---|
'Alice' | 'ALICE' |
'Bob' | 'BOB' |
'Charlie' | 'CHARLIE' |
'Carol' | 'CAROL' |
'David' | 'DAVID' |
Lab: Titleize with Map
Remember the capitalize function? It capitalizes the first letter of a string and makes the whole rest of the string lowercase.
function capitalize(word) {
let firstLetter = word[0];
let restOfWord = word.slice(1);
return firstLetter.toUpperCase() + restOfWord.toLowerCase();
}
Now please try to write a function that capitalizes each word in a string.
titleize("the rain in spain falls MAINLY on the PLAIN")
//=> 'The Rain In Spain Falls Mainly On The Plain'
There is a solution on the next slide, but please try on your own first.
Hint: Inside your titleize function, you could call the existing capitalize function. Or you could "inline" the capitalization code. Or you could do something else!
Solution: Titleize
Here's one way to do it:
function titleize(phrase) {
return phrase.split(' ').map((word) => capitalize(word)).join(' ');
}
Here's another, where the existing capitalize
method is used as is as a mapping function:
function titleize(phrase) {
return phrase.split(' ').map(capitalize).join(' ');
}
And another:
function titleize(phrase) {
let words = [];
let originalWords = phrase.split(' ');
originalWords.forEach((word) => {
words.push(capitalize(word))
});
return words.join(' ');
}
- The first two solutions use method chaining -- taking the result of one method, and immediately calling a method on that result without assigning it to a variable, again and again until you get a final result.
- Method chaining can be very elegant, but it can also be very dense, making the code harder to understand, test, and debug.
- "Unspooling" a method chain into intermediate variables (like example 3) can make the code easier to follow, but it can also make it cluttered and obscure the algorithm.
Whether to use method chaining is a very subjective aesthetic judgement. YMMV!
Reduce
The reduce
method keeps track of a running total (aka accumulator or memo); whatever value the function returns is used as the accumulator for the next pass.
Here's some code that counts the total number of letters across all words in an array:
let names = ['Alice', 'Bob', 'Charlie', 'Carol', 'David'];
const reducer = function(accumulator, word) {
return accumulator + word.length;
};
let totalCount = names.reduce(reducer, 0); //=> 25
The reduce
algorithm can be difficult to follow at first; here's a walkthrough:
Iteration | Accumulator In | Word | Length | Accumulator Out |
---|---|---|---|---|
1 | 0 | 'Alice' | 5 | 0 + 5 = 5 |
2 | 5 | 'Bob' | 3 | 5 + 3 = 8 |
3 | 8 | 'Charlie' | 7 | 8 + 7 = 15 |
4 | 15 | 'Carol' | 5 | 15 + 5 = 20 |
5 | 20 | 'David' | 5 | 20 + 5 = 25 |
See how the accumulator is used to pass information from one iteration to the next?
Iteration Methods in Emoji
(image used with permission by @AccordionGuy based on a tweet by @steveluscher -- with a working implementation 😲 in Swift)