We've touched on arrays a few times in the previous chapters. It's time to explore them in more depth.
An array is an ordered list of elements; each element has a value of any type. You can define an array by placing a list of values between brackets ([]
):
> let myArray = [2, 'Pete', 2.99, 'another string']
This example demonstrates that arrays are heterogeneous; myArray
has both number and string values. Arrays can have anything in them, including objects and even other arrays.
Each element in an array has a unique index number that gives the position of the element in the array. Thus, we can say that arrays are indexed lists as well as ordered lists.
You can refer to any element of an array by its index number. The syntax puts the index number between brackets ([]
) after the array's name or value. For instance, we can get the first element of myArray
this way:
> myArray[0]
= 2
The first element of an array is always at index 0
. Thus, the second element is at index 1
, the third at index 2
, and so on. To access the third element of an array, we can write:
> myArray[2]
= 2.99
If you know that myArray
has 4 elements, it's easy to see that myArray[3]
returns the last element. Suppose you don't know how many elements the array contains. How can you access the last element? It's not hard, but it is a bit tricky. The array's length
property does the job:
> myArray[myArray.length - 1]
= 'another string'
Note that we must use the array's name twice, and, since we must account for the element at index 0
, we must subtract 1 from the length to get the last element's index. Zero-based indexing seems strange at first, and it makes the arithmetic a little tricky, but, once you get used to it—it takes practice—it soon becomes second nature.
Arrays wouldn't be practical if we couldn't modify them. We need the ability to add, remove, and replace elements. The []
operator and some array methods help us accomplish these tasks.
[]
To replace an element of an array, use brackets ([]
) with the assignment operator:
> let array = [1, 2, 3]
> array[1] = 4
= 4
> array
= [ 1, 4, 3 ]
You can also use []
to add new elements to an array:
> array[array.length] = 10
= 10
> array
= [ 1, 4, 3, 10 ]
Note that array[array.length] = value
adds a new value to the end of array
since the previous ending element was at array[array.length - 1]
.
Note that variables declared with const
and initialized to an array are a little strange; while you can't change what array the variable refers to, you can modify the array's contents:
> const MyArray = [1, 2, 3]
> MyArray[1] = 5
> MyArray
= [1, 5, 3]
> MyArray = [4, 5, 6] // Uncaught TypeError: Assignment to constant variable.
This type of behavior can be confusing. It stems from the "variables as pointers" concept that we discuss a little later. Essentially, a const
declaration prohibits changing what thing the const
points to, but it does not prohibit changing the content of that thing. Thus, we can change an element in a const
array, but we can't change which array the const
points to.
If you want the elements of the array to also be const
, you can use the Object.freeze
method:
> const MyArray = Object.freeze([1, 2, 3])
> MyArray[1] = 5
> MyArray
= [1, 2, 3]
Notice how the assignment on line 2 had no effect on the content of the array.
It's important to realize that Object.freeze
only works one level deep in the array. If your array contains nested arrays or other objects, the values inside them can still be changed unless they are also frozen:
> const MyArray = Object.freeze([1, 2, 3, [4, 5, 6]])
> MyArray[3][1] = 0
> MyArray
= [1, 2, 3, [4, 0, 6]]
If you want the sub-array to be constant as well, you have to freeze it:
> const MyArray = Object.freeze([1, 2, 3, Object.freeze([4, 5, 6])])
> MyArray[3][1] = 0
> MyArray
= [1, 2, 3, [4, 5, 6]]
push
The push
method adds one or more elements to the end of an array:
> array.push('a')
= 5 // the new length of the array
> array
= [ 1, 4, 3, 10, 'a' ]
> array.push(null, 'xyz')
= 7
> array
= [ 1, 4, 3, 10, 'a', null, 'xyz' ]
The push
method appends its arguments to the caller (the array), which mutates the caller. It then returns the array's new length. Don't forget that methods and functions perform actions and return values. You must be careful to distinguish between these two things. push
appends elements to the end of the caller array, but it returns the array's updated length. Note that it does not return the modified array! New JavaScript programmers often get confused over this difference and spend hours puzzling over why a function isn't returning the value they expect. Check the documentation if you have any doubt.
concat
The concat
method is similar to push
, but it doesn't mutate the caller. It concatenates two arrays and returns a brand new array that contains all the elements from the original array followed by all of the arguments passed to it:
> array.concat(42, 'abc')
= [ 1, 4, 3, 10, 'a', null, 'xyz', 42, 'abc' ]
> array
= [ 1, 4, 3, 10, 'a', null, 'xyz' ]
How do you know which methods mutate the caller and which ones don't? Most of the time, the documentation has this information; documentation for non-mutating methods may mention that they return a new array. However, don't count on it. The way to be sure is to use the method and examine the results.
pop
The inverse of push
is pop
. While push
adds an element to the end of the array, pop
removes and returns the last element of the array:
> array.pop()
= 'xyz' // removed element value
> array
= [ 1, 4, 3, 10, 'a', null ]
pop
mutates the caller.
splice
The splice
method lets you remove one or more elements from an array, even those that aren't at the end of the array:
let array = [1, 4, 3, 10, "a", null];
> array.splice(3, 2)
[ 10, 'a' ]
> array
= [ 1, 4, 3, null ]
In this example, we delete 2 elements starting at index position 3. splice
mutates the original array and returns a new array that contains the deleted elements.
splice
can also add and insert elements, but we'll leave that for our JavaScript courses.
forEach
JavaScript has a variety of built-in methods that iterate over the contents of an array. In the Loops and Iterating chapter, we introduced the forEach
method. It provides a simple way to iterate over arrays; style guides often recommend it in favor of a for
loop.
To use forEach
, you need a callback function that you pass to forEach
as an argument. A callback function is a function that you pass to another function as an argument. The called function invokes the callback function when it runs. The forEach
method invokes its callback once for each element, passing it the element's value as an argument. forEach
always returns undefined.
A callback is a function that you pass to another function as an argument. The called function subsequently invokes the callback function at certain times while it runs.
let array = [1, 2, 3];
array.forEach(function(num) {
console.log(num); // on first iteration => 1
// on second iteration => 2
// on third iteration => 3
}); // returns `undefined`
This code invokes the callback function once for each element in the array. forEach
, during each iteration, invokes the callback with the element's value as an argument. The callback then logs it to the console. In the end, forEach
returns undefined
.
We can also use an arrow function instead of a function expression, which makes our code compact and, when you're familiar with the syntax, more readable.
let array = [1, 2, 3];
array.forEach(num => console.log(num));
We can also perform more complex operations:
let array = [1, 2, 3];
array.forEach(num => console.log(num + 2)); // on first iteration => 3
// on second iteration => 4
// on third iteration => 5
map
forEach
works well when you want to use the values of an array's elements. Suppose, though, that you want to create a new array whose values depend on the original contents of the array. For instance, suppose you want to create a new array that contains the squares of all the numbers in the calling array. With forEach
, you might end up with something like this:
> let numbers = [1, 2, 3, 4]
> let squares = [];
> numbers.forEach(num => squares.push(num * num));
> squares
= [ 1, 4, 9, 16 ]
> numbers
= [ 1, 2, 3, 4 ]
That works well enough, but the callback now has a side effect: it modifies the squares
variable, which isn't part of the callback function. Side effects sometimes lead to trouble. Consider what happens if you ran the forEach
call again after running the above code:
> numbers.forEach(num => squares.push(num * num));
> squares
= [ 1, 4, 9, 16, 1, 4, 9, 16 ]
We now have two copies of the squares since we forgot to reset squares
to an empty array.
The map
method handles this situation more cleanly:
> let numbers = [1, 2, 3, 4]
> let squares = numbers.map(num => num * num);
> squares
= [ 1, 4, 9, 16 ]
> squares = numbers.map(num => num * num);
= [ 1, 4, 9, 16 ]
The first 4 lines of this code have the same result as the previous example using forEach
. However, map
returns a new array that contains one element for each element in numbers
, with each element set to the return value of the callback: the squares of the numbers in this case. This code is more compact than the forEach
code, and, better yet, it has no side effects; the callback doesn't update squares
(the return value of map
does that), and we don't have to reset the variable if we rerun the same code.
forEach
and map
are important methods, but they can confuse beginners. The main thing to remember is that forEach
performs simple iteration and returns undefined
, while map
transforms an array's elements and returns a new array with the transformed values.
filter
The filter
method is another array iteration method. It returns a new array that includes all elements from the calling array for which the callback returns a truthy value. That's a mouthful. Some code should help clarify what filter
does:
> let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2]
> numbers.filter(num => num > 4)
= [ 5, 6, 7, 8, 9, 10 ]
> numbers
= [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2 ]
filter
iterates over the elements of the array. During each iteration, it invokes the callback function, using the value of the current element as an argument. If the callback returns a truthy value, filter
appends the element's value to a new array. Otherwise, it ignores the element's value and does nothing. When filter
finishes iterating, it returns the array of selected elements: the elements for which the callback returned a truthy value. In our example, filter
selects all of the elements with a value greater than 4.
filter
doesn't mutate the caller.
reduce
The reduce
method effectively reduces the contents of an array to a single value. It is, perhaps, one of the hardest array iteration methods to understand, but it is also one of the most fundamental. You can build forEach
, map
, and filter
with reduce
, as well as a number of other iterative methods defined for Arrays.
reduce
takes two arguments: a callback function and a value that initializes something called the accumulator. In its simplest form, the callback function takes two arguments: the current value of the accumulator and an element from the array. It returns a value that will be used as the accumulator in the next invocation of the callback. That sounds more complicated than it is, so let's take a look at two simple uses of reduce
:
> let arr = [2, 3, 5, 7]
> arr.reduce((accumulator, element) => accumulator + element, 0)
= 17
> arr.reduce((accumulator, element) => accumulator * element, 1)
= 210
The first invocation computes the sum of all the values in the array, e.g., 2 + 3 + 5 + 7
. To get us started, we initialize the accumulator to 0. Thus, on the first invocation of the callback function, accumulator
is 0
and element
is 2
. The callback returns 2
, which becomes the new accumulator
value when we invoke the callback again, this time with the element 3
. That invocation, in turn, returns 5
. This process continues until the final return value is 17
.
The second invocation of reduce
computes the product of the numbers in the array (2 * 3 * 5 * 7
), starting out with 1
as the accumulator. (If we started with 0
as the accumulator, the final return value would be 0
since 0
times anything is 0
.)
The reduce
function isn't limited to computing numbers: you can also use it to compute strings, objects, arrays: anything. Here's an example with strings:
> let strings = ['a', 'b', 'c', 'd']
> strings.reduce((accumulator, element) => {
... return accumulator + element.toUpperCase()
... }, '');
= 'ABCD'
reduce
does not mutate the caller. (It is possible that the callback might mutate the caller, but that's inadvisable, and not reduce
's fault.)
JavaScript arrays have some odd properties that might catch you by surprise. If you've worked with arrays in other languages, the oddities may be especially surprising.
You've already met one oddity: indexes start at 0
. Most programming languages use zero-based indexes, so it's not too odd. However, learners often stumble on this aspect of using arrays.
The length
property always returns a number that is one greater than the greatest used index position of the array. For instance, if an element exists at index position 124
and there are no other elements with greater index values, then the array's length
is 125.
Arrays are objects, which we'll meet in the next chapter. One side effect of this is that the typeof
operator doesn't return 'array'
when applied to an array:
> let arr = [1, 2, 3]
> typeof arr
= 'object'
If you really need to detect whether a variable references an array, you need to use Array.isArray
instead:
> let arr = [1, 2, 3]
> Array.isArray(arr)
= true
If you change an array's length
property to a new, smaller value, the array gets truncated; JavaScript removes all elements beyond the new final element.
If you change an array's length
property to a new, larger value, the array expands to the new size. The new elements do not get initialized, which leads to some strange behavior:
> let arr = []
> arr.length = 3
> arr
= [ <3 empty items> ]
> arr[0]
= undefined
> arr.filter(element => element === undefined)
= []
> arr.forEach(element => console.log(element)) // no output
= undefined
> arr[1] = 3
> arr
= [ <1 empty item>, 3, <1 empty item> ]
> arr.length
= 3
> arr.forEach(element => console.log(element))
= 3
= undefined
> Object.keys(arr)
= ['1']
In general, JavaScript treats unset array elements as missing, but the length
property includes the unset elements.
You can create array "elements" with indexes that use negative or non-integer values, or even non-numeric values:
> arr = [1, 2, 3]
= [ 1, 2, 3 ]
> arr[-3] = 4
= 4
> arr
= [ 1, 2, 3, '-3': 4 ]
> arr[3.1415] = 'pi'
= 'pi'
> arr["cat"] = 'Fluffy'
= 'Fluffy'
> arr
= [ 1, 2, 3, '-3': 4, '3.1415': 'pi', cat: 'Fluffy' ]
These "elements" aren't true elements; they are properties on the array object, which we'll discuss later. Only index values (0, 1, 2, 3, and so on) count toward the length of the array.
> arr.length
= 3
Since arrays are objects, you can use the Object.keys
method to return an array's keys -- its index values -- as an array of strings. Even negative, non-integer, and non-numeric indexes are included.
> arr = [1, 2, 3]
> arr[-3] = 4
> arr[3.1415] = 'pi'
> arr["cat"] = 'Fluffy'
> arr
= [ 1, 2, 3, '-3': 4, '3.1415': 'pi', cat: 'Fluffy' ]
> Object.keys(arr)
= [ '0', '1', '2', '-3', '3.1415', 'cat' ]
One quirk of this method is that it treats unset values in arrays differently from those that merely have a value of undefined
. Unset values are created when there are "gaps" in the array; they show up as empty items until you try to use their value:
> let a = new Array(3);
> a
= [ <3 empty items> ]
> a[0] === undefined;
= true
> let b = [];
> b.length = 3;
> b
= [ <3 empty items> ]
> b[0] === undefined;
= true
> let c = [undefined, undefined, undefined]
> c
= [ undefined, undefined, undefined ]
> c[0] === undefined;
= true
While the length
property of Array includes unset values in the count, Object.keys
only counts those values that have been set to some value:
> let aKeys = Object.keys(a)
> a.length
= 3
> aKeys.length;
= 0
> let bKeys = Object.keys(b)
> b.length
= 3
> bKeys.length;
= 0
> let cKeys = Object.keys(c)
> c.length
= 3
> cKeys.length;
= 3
It's worth spending some time and effort to memorize these behaviors.
Array elements can contain anything, including other arrays. You can create arrays with arrays inside them and even arrays inside those inner arrays. Suppose you want to track all of the teams playing in a mixed doubles tennis tournament. You might create an array like this.
> let teams = [['Joe', 'Jennifer'], ['Frank', 'Molly'], ['Dan', 'Sarah']]
You can now find the teams by index.
> teams[2]
= [ 'Dan', 'Sarah' ]
If you want to retrieve the second element of teams[2]
, you can append [1]
to the expression:
> teams[2][1]
= 'Sarah'
What do you think the following expression returns?
> [1, 2, 3] === [1, 2, 3]
A reasonable answer is that it returns true
. After all, the arrays look identical. JavaScript, however, isn't reasonable in this case: the expression returns false
.
> [1, 2, 3] === [1, 2, 3]
= false
How about the following comparison?
> let a = [1, 2, 3]
> let b = a
> a === b
Curiously, this comparison evaluates as true. What's happening here?
JavaScript treats two arrays as equal only when they are the same array: they must occupy the same spot in memory. This rule holds for JavaScript objects in general; objects must be the same object. For this reason, the second example returns true
while the first one returns false
. Assigning a
to b
makes b
refer to the same array as a
; it doesn't create a new array.
If that last paragraph confused you, come back to this section after you've read Variables as Pointers in the More Stuff chapter. If you want, you can even read it now.
It's important to realize that the previous discussion concerns arrays (and other JavaScript objects). The situation with primitive values is different and less surprising.
At first glance, you might say that the arrays in the first example are also "the same array," but they're not. They're two different arrays that happen to have the same values. However, they occupy distinct positions in memory, so they aren't the same array, and thus aren't equal.
Given this behavior, how do you check whether two arrays have the same elements? One option is to create a function that compares the elements of one array with the corresponding elements in the other:
function arraysEqual(arr1, arr2) {
if (arr1.length !== arr2.length) return false;
for (let i = 0; i < arr1.length; i += 1) {
if (arr1[i] !== arr2[i]) {
return false;
}
}
return true;
}
console.log(arraysEqual([1, 2, 3], [1, 2, 3])); // => true
console.log(arraysEqual([1, 2, 3], [4, 5, 6])); // => false
console.log(arraysEqual([1, 2, 3], [1, 2, 3, 4])); // => false
arraysEqual
takes two arrays and returns false
when an element in one array doesn't equal the corresponding element in the other. Otherwise, it returns true
. It returns false
right away if the arrays have different lengths; taking care of this case first simplifies the rest of the function.
arraysEqual
works best when all elements in both arrays are primitive values. If any pair of elements has a non-primitive value (an array or object), arraysEqual
might not return the result you expect:
> arraysEqual([1, 2, [3, 4], 5], [1, 2, [3, 4], 5])
= false
This section introduces some additional built-in methods that JavaScript Arrays provide. You should bookmark that page: you will use it often.
The includes
method determines whether an array includes a given element:
> let a = [1, 2, 3, 4, 5]
> a.includes(2)
= true
> a.includes(10)
= false
Internally, includes
uses ===
to compare elements of the array with the argument. That means we can't use includes
to check for the existence of a nested array or an object unless we have a reference to the same object or array we're looking for:
> let inner = [3, 4];
> let a = [1, 2, inner, 5]
> a.includes([3, 4])
= false
> a.includes(inner)
= true
The indexOf
method searches an array for an element with a given value and returns the index of the found element. If the element is not found, indexOf
returns -1
.
> let a = ['a', 'b', 'c', 'd', 'e']
> a.indexOf('c')
= 2
> a.indexOf('x')
= -1
As with includes
, indexOf
internally uses ===
to compare elements of the array with the argument. That means we can't use indexOf
to check for the existence of a nested array or an object unless we have a reference to the same object or array we're looking for:
> let inner = [3, 4];
> let a = [1, 2, inner, 5]
> a.indexOf([3, 4])
= -1
> a.indexOf(inner)
2
By default, indexOf
only looks for the first occurrence of a value in the array. If you want to look beyond the first occurrence, you need to give indexOf
a starting index:
> let a = ['a', 'b', 'c', 'b', 'e']
> a.indexOf('b')
= 1
> a.indexOf('b', 2)
= 3
This last example begins the search at index position 2.
The sort
method is a handy way to rearrange the elements of an array in sequence. It returns a sorted array.
> let a = ["e", "c", "h", "b", "d", "a"]
> a.sort()
= [ 'a', 'b', 'c', 'd', 'e', 'h' ]
Use node
or the console to test whether sort
is destructive, i.e., does it mutate the caller? (It does, but check for yourself.)
We'll learn more about sort
and how it works in a later course.
The slice
method - not the splice
method you met earlier - extracts and returns a copied portion of the array. It takes two optional arguments. The first is the index at which extraction begins, while the second is where extraction ends:
> let fruits = ['mango', 'orange', 'banana', 'pear', 'apple']
> fruits.slice(1, 3)
= [ 'orange', 'banana' ]
> fruits.slice(2) // second argument defaults to rest of array
= [ 'banana', 'pear', 'apple' ]
> fruits.slice() // no arguments duplicates the array
= [ 'mango', 'orange', 'banana', 'pear', 'apple' ]
If you omit the second argument, slice
returns the rest of the array starting with the index given by the first argument. With the second argument, it returns the elements up to but excluding that index. (Contrast this detail with how splice
treats its second argument.) If you don't provide any arguments at all, slice
returns a copy of the entire array: that is, it returns a new array with the same elements as the original. Full copies of an array are useful when you want to use a destructive method on an array but you don't want to modify the original. You can mutate the copy and keep the original.
The reverse
method reverses the order of an array.
> let numbers = [1, 2, 3, 4]
> numbers.reverse()
= [ 4, 3, 2, 1 ]
> numbers
= [ 4, 3, 2, 1 ]
reverse
is destructive: it mutates the array. As mentioned in the previous section, you can use slice
with no arguments if you don't want to mutate the original array:
> let numbers = [1, 2, 3, 4]
> let reversedNumbers = numbers.slice().reverse();
> reversedNumbers
= [ 4, 3, 2, 1 ]
> numbers
= [ 1, 2, 3, 4 ]
Arrays are a valuable data structure. You'll see them all the time in real-world programs; nearly every useful program uses arrays at some point. JavaScript's array type has plenty of built-in methods that can perform the basic operations that programmers need every day.
In the following code, what are the final length
values for array1
, array2
, array3
, array4
, and array5
?
let array1 = [1, 2, undefined, 4];
let array2 = [1];
array2.length = 5;
let array3 = [];
array3[-1] = [1];
let array4 = [1, 2, 3, 4, 5];
array4.length = 3;
let array5 = [];
array5[100] = 3;
The length of array1
is 4
. The length is the highest index position that has a value, plus 1
. In this case, the highest index position that has a value is 3
; add 1
to that, and we get the length value of 4
.
The length of array2
is 5
. You can set the length of an array. Even if the highest index position that has a value assigned is 0
, assigning a new length of 5
overrides that length. For now, you can think of the resulting array as having 5 elements of which the last 4 have a value of undefined
. In actuality, the array still has only one element, but has 4 gaps at the end -- the gaps aren't real elements and take up very little memory. We'll learn more about this in the Core Curriculum.
The length of array3
is 0
. Index positions must be non-negative integers starting from 0
. Negative and non-integer indexes don't get taken into account when determining an array's length.
The length of array4
is 3
. Contrast this with array2
. When you set an array to a length that is shorter than its current length, the array gets truncated to the new length. In this example, JavaScript truncates the array by removing the last two elements, leaving a total of 3 elements.
The length of array5
is 101
. As already noted, the length is the highest index position that has a value, plus 1
. In this case, the highest index position that has a value is 100
, so the length is 101
.
Video Walkthrough
Log all of the even values from myArray
to the console.
let myArray = [1, 3, 6, 11, 4, 2,
4, 9, 17, 16, 0];
Expected Output
6
4
2
4
16
0
for (let i = 0; i < myArray.length; i += 1) {
let value = myArray[i];
if (value % 2 === 0) {
console.log(value);
}
}
Our approach is straightforward: we iterate over all the elements in the array and check whether each element is even. You can also use forEach
and let JavaScript take care of the indexing:
myArray.forEach(function(value) {
if (value % 2 === 0) {
console.log(value);
}
});
Video Walkthrough
Let's make the problem a little harder. In this problem, we're again interested in even numbers, but this time the numbers are in nested arrays in a single outer array.
let myArray = [
[1, 3, 6, 11],
[4, 2, 4],
[9, 17, 16, 0],
];
for (let i = 0; i < myArray.length; i += 1) {
for (let j = 0; j < myArray[i].length; j += 1) {
let value = myArray[i][j];
if (value % 2 === 0) {
console.log(value);
}
}
}
Our approach is again straightforward, but it's a bit verbose. However, the chained brackets in myArray[i][j]
make it visually explicit that we're dealing with a two-dimensional nested array.
As before, we can also use forEach
to abstract away the messy details of indexes and stopping conditions:
myArray.forEach(function(nestedArray) {
nestedArray.forEach(function(value) {
if (value % 2 === 0) {
console.log(value);
}
});
});
That's a bit complicated at this stage in your development, but have a go at trying to figure out how it works.
Video Walkthrough
Let's try another variation on the even-numbers theme.
We'll return to the simpler one-dimensional array. In this problem, we want to use the map
function to create a new array that contains one element for each element in the original array. If the element is an even value, then the corresponding element in the new array should contain the string 'even'
; otherwise, the element in the new array should contain 'odd'
.
Example
let myArray = [
1, 3, 6, 11,
4, 2, 4, 9,
17, 16, 0,
];
Expected Output
[
'odd', 'odd', 'even', 'odd',
'even', 'even', 'even', 'odd',
'odd', 'even', 'even',
]
If you have trouble using map
to accomplish this, try it using a regular for loop instead.
let newArray = myArray.map(function(value) {
if (value % 2 === 0) {
return 'even';
} else {
return 'odd';
}
});
With a For Loop
let newArray = [];
for (let i = 0; i < myArray.length; i += 1) {
let value = myArray[i];
if (value % 2 === 0) {
newArray.push('even');
} else {
newArray.push('odd');
}
}
Our approach is again straightforward: we iterate over all the elements in the array and check whether each element is even. If it's even, we either return 'even'
from the function we passed to map
, or push 'even'
onto the newArray
. Otherwise, we return or push a value of 'odd'
.
Video Walkthrough
Write a findIntegers
function that takes an array argument and returns an array that contains only the integers from the input array. Use the filter
method in your function.
Example
let things = [1, 'a', '1', 3, NaN, 3.1415, -4, null, false];
let integers = findIntegers(things);
console.log(integers); // => [1, 3, -4]
You can use Number.isInteger(value)
to determine whether a numeric value
is an integer. It returns true
if the value is an integer, false
otherwise.
function findIntegers(array) {
return array.filter(function(element) {
return Number.isInteger(element);
});
}
Video Walkthrough
Use map
and filter
to first determine the lengths of all the elements in an array of string values, then discard the even values (keep the odd values).
let arr = ['a', 'abcd', 'abcde', 'abc', 'ab'];
console.log(oddLengths(arr)); // => [1, 5, 3]
Note that it is possible to solve this problem without using map
. However, our intent is to show how you can combine multiple functions to achieve a desired result.
Your first step should be to create an array of the lengths, e.g., [1, 4, 5, 3, 2]
.
function oddLengths(strings) {
let lengths = strings.map((letters) => letters.length);
let oddLengths = lengths.filter((number) => number % 2 === 1);
return oddLengths;
}
let arr = ['a', 'abcd', 'abcde', 'abc', 'ab'];
console.log(oddLengths(arr));
Video Walkthrough
Use reduce
to compute the sum of the squares of all of the numbers in an array:
let array = [3, 5, 7];
console.log(sumOfSquares(array)); // => 83
Note that 83
is 3*3 + 5*5 + 7*7
.
function sumOfSquares(numbers) {
return numbers.reduce((accumulator, number) => {
return accumulator + number * number;
}, 0);
}
let array = [3, 5, 7];
console.log(sumOfSquares(array)); // => 83
What happens if you forget to provide an initial value of 0 for the accumulator? Take a look at the MDN Documentation for reduce
and see if you can determine what sumOfSquares
would return if you omitted the initial accumulator value.
Video Walkthrough
This problem is more challenging than most in this book. Don't worry if you can't solve it on your own.
Write a function similar to the oddLengths
function from Exercise 6, but don't use map
or filter
. Instead, try to use the reduce
method.
let arr = ['a', 'abcd', 'abcde', 'abc', 'ab'];
console.log(oddLengths(arr)); // => [1, 5, 3]
Use an array as the accumulator. Ideally, you should be able to use the return value of reduce
as the return value of the function.
function oddLengths(strings) {
return strings.reduce((filteredNumbersArray, letters) => {
let length = letters.length;
if (length % 2 === 1) {
filteredNumbersArray.push(length);
}
return filteredNumbersArray;
}, []);
}
let arr = ['a', 'abcd', 'abcde', 'abc', 'ab'];
console.log(oddLengths(arr));
Video Walkthrough
Without using a for
, while
, or do/while
loop, write some code that checks whether the number 3
appears inside these arrays:
let numbers1 = [1, 3, 5, 7, 9, 11];
let numbers2 = [];
let numbers3 = [2, 4, 6, 8];
Return true
or false
depending on each result.
> numbers1.includes(3);
= true
> numbers2.includes(3);
= false
> numbers3.includes(3);
= false
Video Walkthrough
Write some code to replace the value 6
in the following array with 606
:
let arr = [
["hello", "world"],
["example", "mem", null, 6, 88],
[4, 8, 12]
];
You don't have to search the array. Just write an assignment that replaces the 6
.
> arr[1][3] = 606;
Video Walkthrough