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 arrayarray.reverse()
is a method that reverses the ordering of all elements in the array See MDN: Array to see a lot more array 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 |
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
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:
beginsWithC
function returns true
or false
find
method returns an item (from the array)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?
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'
)
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' ]
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.
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' |
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!
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(' ');
}
Whether to use method chaining is a very subjective aesthetic judgement. YMMV!
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?
(image used with permission by @AccordionGuy based on a tweet by @steveluscher -- with a working implementation 😲 in Swift)
/