facebook google plus twitter
Webucator's Free Advanced JavaScript Programming Tutorial

Lesson: Advanced Arrays

Welcome to our free Advanced JavaScript Programming tutorial. This tutorial is based on Webucator's Advanced JavaScript Programming course.

The Array class in JavaScript offers a range of useful static and instance methods.

Lesson Goals

  • Use built-in static and instance methods from the Array class.
  • Write arrow functions.
  • Learn how to use iterators.

Arrays

Arrays in JavaScript are, of course, collections of values with numeric, zero-based indices. We can create an array variable by assigning it an array literal:

var a = ['apple', 'banana', 'cherry'];

or by explicitly instantiating a new Array object:

var a = new Array('apple', 'banana', 'cherry');

and get the same result. We access elements of the array with the square-bracket operator, both to get and to set values:

document.write(a[0]);
a[1] = 'orange';

We can store anything - different anythings - in the same array: the first element of any array might contain a string, the second an integer, the third an object, the fourth a function, etc. The length property gives us the current size of an array:

document.write(a.length);

Following this brief review of arrays, we will in this lesson take a look at some perhaps less well-known, more recent, and useful methods from the built-in JavaScript Array class.

Arrays Methods

We'll look into the methods listed in the table below. Note that Array.method() implies that the method is static (i.e. belongs to the class Array) and is applied like Array.method(argument). Those methods with signature Array.prototype.method() means the method should be applied to an instances, like arr.method().

Array Methods
Method Description
Array.from(collection, mapFunction?, thisArgument?) Creates a new Array instance from a array-like collection, like the arguments of a function, a Set, a Map, a String, etc. Optionally apply a function mapFunction on every element and optionally apply a value (thisArgument) to use as this when executing mapFunction.
Array.isArray(object) Returns true if object is an array, false if not.
Array.of(element0?, element1?, ...) Creates a new Array instance from its arguments (any number of arguments). Differs from the default Array contructor in that Array.of(3) creates a new instance with single element 3, where new Array(3) creates a new instance with three elements all with value undefined.
Array.prototype.fill(value, start?, end?) Fills all elements of an array with a static value from start to end, inclusive of start and exclusive of end. start defaults to 0 and end defaults to the array's length.
Array.prototype.filter(callbackFunction, thisArgument?) Creates new array with all elements for which the application of the callbackFunction returns true, with optional thisArgument value to use as this when executing the callbackFunction.
Array.prototype.find(callbackFunction, thisArgument?) Returns the first value from the array for which callbackFunction returns true, with optional thisArgument value to use as this when executing the callbackFunction. Returns undefined is no element is found.
Array.prototype.forEach(callbackFunction, thisArgument?) Executes callbackFunction once per each array element, with optional thisArgument value to use as this when executing callbackFunction.
Array.prototype.indexOf(elementToFind, start?) Returns the first index at which elementToFind can be found, or -1 if elementToFind is not present. The optional start parameter (defaulting to 0) is the index from which to start searching.
Array.prototype.keys() Returns a new Array Iterator containing the keys from the array.
Array.prototype.map(callbackFunction, thisArgument?) Creates new array from the application of callbackFunction on each element of the original array, with optional thisArgument value to use as this when executing callbackFunction.
Array.prototype.reduce(callbackFunction, initialValue?) Returns a single value from the application of callbackFunction to each element of the array against an accumulated value, with optional initialValue parameter to use at the starting accumulated value. Useful, for example, to find the sum of the elements of an array.

Iterators and Arrow Functions

Before we take a look at Array methods, we'll look first at a few key concepts that will come up in our review of those methods.

Iterators

The ECMAScript 2015 standard of JavaScript introduced iterators, defined as objects with a next() method that returns an object of the format {value:Any, done: Boolean}. In short, iterators are objects that know how to access items one at a time, keeping track of the current position in the sequence and knowing when the end has been reached.

Abstracting this functionality gives us a way to handle disparate types of collections with the same control structures. The built-in types Array, Map, Set, and String all define a default iteration behavior.

If iter were an iterator with values "A", "B", and "C", then the following code would demonstrate how we might use the next() method:

iter.next() // would return { value: 'A', done: false }
iter.next() // would return { value: 'B', done: false }
iter.next() // would return { value: 'C', done: false }
iter.next() // would return { value: undefined, done: true }

Later in this lesson we will see some examples of Array methods that return iterators.

Arrow Functions

ECMAScript 2015 introduced arrow functions (sometimes called "fat arrow functions") to JavaScript, offering a more-concise syntax for writing function expressions:

(x, y) => x + y

The code above reads "let there be an anonymous function expression which takes two parameters, x and y, and returns x+y". This shorter syntax is especially useful for simple callback functions. With just one parameter we can optionally omit the parentheses:

(x) => x + 3
x => x + 3

Note that you need to have the parentheses when there are no arguments declared.

The arrow functions above are identical, both returning the value of three more than the parameter x.

Let's take a look at an example:

Code Sample:

AdvancedArrays/Demos/arrow-functions.html
<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title>Arrow Functions</title>
</head>
<body>
	<h1>Arrow Functions</h1>
	<script>
		// a function expression:
		var foo = function(a, b) {
			return 3*a + b;
		}
		document.write( foo(1,2) );

		document.write( '<hr>' );

		// written as an arrow function:
		let bar = (a, b) => 3*a + b;
		document.write( bar(1,2) );

		document.write( '<hr>' );
		document.write( '<hr>' );

		// the real value of arrow functions comes when using callback functions:
		var arr1 = [1, 2, 3, 4];
		var arr1Squares = arr1.map(function(i) {
			return i*i;
		});
		document.write( arr1Squares );

		document.write( '<hr>' );

		var arr2 = [1, 2, 3, 4];
		// using arrow function:
		var arr2Squares = arr2.map(i => i*i);
		document.write( arr2Squares );

	</script>
</body>
</html>

Code Explanation

In the first set of examples, we write function expression foo in the ECMAScript 3 (and 5) syntax manner; on line 14 we write the value of foo(1,2) to the screen. We then do the same thing with an arrow function, assigning it to variable bar and write bar(1,2) to the screen.

While this first example illustrates how to use arrow functions, it's not much help to us here: function foo was already pretty simple and using an arrow function to create function bar isn't much shorter (and, one might argue, is a little harder to read.)

The real value of arrow functions comes when using them for callback functions. In the second set of examples, we use the Array method map to create a new array from an existing array by squaring each element of the first array. In the first example, on line 27, we use an anonymous function as the callback parameter for map, returning the square of each element. We then write the same thing using arrow functions, supplying i => i*i as the callback function parameter for map: it's the same anonymous function but the short syntax makes for shorter, clearer code.

Keep in mind that, as with any ECMAScript 2015 feature, arrow functions are a relatively-recent addition to JavaScript and won't be supported by some older browsers. As we mentioned earlier in this course, caniuse.com is a great resource for determining the availability of ECMAScript 2015 and ECMAScript 2016 features in various browsers.

Static Array Methods

Array.isArray()

A useful - and fairly simple - static method of the Array class: isArray returns true if its argument is an array:

Array.isArray(object)

The following would all return true:

Array.isArray(new Array());
Array.isArray([]);
Array.isArray([17, 100, 3]);

and the following would all return false:

Array.isArray(17);
Array.isArray('Jane Doe');
Array.isArray();
Array.isArray(null);
Array.isArray({fname:'Jane', lname:'Doe'});
Array.isArray(false);

Since none of a number, a string, null, an Object, nor a Boolean value is an array.

Array.from()

The static from method allows us to create a new array from an array-like or iterable object:

Array.from(collection, mapFunction?, thisArgument?)

The first parameter of from is the object to use to create the new array; optionally, we can supply a callback function (mapFunction) to call on each element and a value for the callback function to use as this.

We might use from to create an array from a string, from a Map or Set object, or from the arguments of a function (recall that the local arguments object for any function is similar to an array, but does not have any Array properties except length).

The following examples illustrate the use of from to create arrays:

Code Sample:

AdvancedArrays/Demos/from.html
<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title>Array.from</title>
</head>
<body>
	<h1>Array.from</h1>
	<p>sample markup code</p>
	<ul>
		<li>item 1</li>
		<li>item 2</li>
		<li>item 3</li>
		<li>item 4</li>
	</ul>
	<script>
		function arrayFromArguments() {
			return Array.from(arguments);
		}
		// create array from the arguments supplied to a function:
		document.write(arrayFromArguments(11, 21, -17));
		document.write('<br>Array?: ' + Array.isArray(arrayFromArguments(11, 21, -17)));

		document.write('<hr>');

		var str = 'I am a string';
		// create array from the characters in a string:
		document.write(Array.from(str));
		document.write('<br>Array?: ' + Array.isArray(Array.from(str)));

		document.write('<hr>');

		var m = new Map();
		m.set(0, 'apple');
		m.set(1, 'banana');
		m.set(2, 'cherry');
		// create multidimensional array from map:
		document.write(Array.from(m));
		console.log(Array.from(m));
		document.write('<br>Array?: ' + Array.isArray(Array.from(m)));

		document.write('<hr>');

		// create array from collection of DOM elements:
		document.write(Array.from(document.getElementsByTagName('li')));
		document.write('<br>Array?: ' + Array.isArray(Array.from(str)));

		document.write('<hr>');

		// create array from array, with map function
		document.write(Array.from([10, 20, 30], function(i) {
			return i*i;
		}));
		document.write('<br>Array?: ' + Array.isArray(Array.from(str)));

		document.write('<hr>');

		// same as above, but using arrow function:
		document.write(Array.from([10, 20, 30], i => i*i));

	</script>
</body>
</html>

Code Explanation

On line 11 we call function arrayFromArguments which returns an array from its arguments object; calling arrayFromArguments(11, 21, -17) thus returns the array [11, 21, -17].

On line 28 we create an array from the string 'I am a string', getting the array ['I',' ','a','m',' ','a',' ','s','t','r','i','n','g'].

On line 38 we use Array.from to create an array from a Map object, getting a multidimensional array. Here's the output from our call to console.log(Array.from(m)) on line 39, using the developer tools in our browser to inspect the value of the multidimensional array that is created from the Map:

console output of Array.from(map)

On line 45 we create an array from the collection of <li> elements (at the top of our HTML page).

The final two examples show how can apply a map function to each element from which we create the array. On line 51 we create a new array containing the squares of each element of the original array; we do the same thing on line 59 but here use an arrow function as the map function.

Note that we use Array.isArray to make sure that the object returned by each of our calls to Array.from is indeed an array.

Array.of()

The static of method lets use create a new array from an enumerated list of elements:

Array.of(element0?, element1?, ...)

with as many (or few) elements as we wish. The difference between creating an array from Array.of and the Array.new constructor is that Array.new(17) creates an array with 17 elements (each with value undefined), whereas Array.of(17) creates a single-element array with value 17.

The following examples show the use of Array.of:

Code Sample:

AdvancedArrays/Demos/of.html
<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title>Array.of</title>
</head>
<body>
	<h1>Array.of</h1>
	<script>
		//these two arrays have the same elements
		var array1 = Array.of(10, 20, 30);
		var array2 = new Array(10, 20, 30);
		document.write(array1);
		document.write('<br>' + array2);
		
		document.write('<hr>');

		//these two arrays differ
		var array3 = Array.of(5);
		var array4 = new Array(5);
		document.write(array3);
		document.write('<br>' + array4);

	</script>
</body>
</html>

Code Explanation

In the first example, we create array1 (using Array.of) and array2 (using Array.new); array1 and array2 contain the same elements.

In the second example, we demonstrate the difference when Array.new is called with a single value: Array.of(5) is an array with one element (with value 5), while new Array(5) is an array with five elements, each with value undefined.

Array Prototype Methods

The following methods are applied to instances of Array.

Array.prototype.fill()

The fill method mutates (changes) the array instance to which it is applied, filling all elements (or some portion of the elements, as dictated by the start and end parameters) with the specified value parameter:

Array.prototype.fill(value, start?, end?)

The start value is the index from which to start filling; the end value is the index up to which to (but not including) to end filling. Note that indexing here is 0-based (the first element has index 0), and that the optional start? and end? parameters default to 0 and the length of the array.

A negative start value is interpreted as length + start. Similarly, a negative end value is interpreted as length + end.

The following example illustrates the use of fill:

Code Sample:

AdvancedArrays/Demos/fill.html
<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title>Array.prototype.fill</title>
</head>
<body>
	<h1>Array.prototype.fill</h1>
	<script>
		document.write([1, 2, 3, 4].fill(17));
		document.write('<hr>');
		document.write([1, 2, 3, 4].fill(17, 2));
		document.write('<hr>');
		document.write([1, 2, 3, 4].fill(17, 0, 2));
		document.write('<hr>');
		var fruits = ['apple', 'banana', 'cherry', 'date', 'elderberry'];
		document.write(fruits.fill('zucchini', -3, -1));

	</script>
</body>
</html>

Code Explanation

On line 10, we fill a four-element array of integers with the value 17.

On line 12, we fill the same array with the value 17, starting at index 2 - thus filling the latter two (indices 2 and 3) elements.

On line 14, we fill the same array with the value 17, starting at index 0 and ending before index 2 - thus filling the first two (indices 0 and 1) elements.

In the last example, on line 16, we fill a five-element array of strings with the value zucchini using negative start and end parameters: -3 for start means "start filling at the third element from the end" and -1 for end means "stop filling before the last element".

Array.prototype.filter()

The filter method returns a new array from the array to which it is applied (without changing the original array) where each element passes a test, supplied to the method as a callback function:

Array.prototype.filter(callbackFunction, thisArgument?)

The function passed as callbackFunction should return true or false as it is applied to each element. Optionally, we can supply a value for callbackFunction to use as this.

Code Sample:

AdvancedArrays/Demos/filter.html
<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title>Array.prototype.filter</title>
</head>
<body>
	<h1>Array.prototype.filter</h1>
	<script>
		var arrNums = [10, -3, 39, 25, 88];
		function greaterThan17(val) {
			return val > 17;
		}
		var arrBiggerThan = arrNums.filter(greaterThan17);
		document.write(arrBiggerThan);
		
		
		document.write('<hr>');

		
		var arrDates = [];
		arrDates.push(new Date(2016, 04, 01));
		arrDates.push(new Date(2015, 01, 19));
		arrDates.push(new Date(1977, 11, 13));
		arrDates.push(new Date(1999, 01, 01));
		function laterThan(d) {
			var dateTest = new Date(1990, 01, 01);
			return d > dateTest;
		}
		var arrDatesLaterThan = arrDates.filter(laterThan);
		document.write(arrDatesLaterThan);


		document.write('<hr>');


		var arrPossibleSquares = [1, 2, 9, 16, 24, 36, 41];
		function isPerfectSquare(n) {
			return n > 0 && Math.sqrt(n) % 1 === 0;
		}
		var arrPerfectSquares = arrPossibleSquares.filter(isPerfectSquare);
		document.write(arrPerfectSquares);


		document.write('<hr>');


		var arr = [10, 21, 4, 88, 4, 10, 10, 14, 37, 21];
		function isUnique(val) {
			var count = 0;
			for (var i in arr) {
				if (val == arr[i]) {
					count++;
					if (count > 1) {
						return false;
					}
				}
			}
			return true;
		}
		var uniqueElements = arr.filter(isUnique, arr);
		document.write(uniqueElements);

	</script>
</body>
</html>

Code Explanation

Our filter.html file shows four examples of using filter. The first example uses a callback function, greaterThan17, to return true for elements greater than 17. The expression arrBiggerThan = arrNums.filter(greaterThan17) creates a new array arrBiggerThan which contains all of the elements from arrNums which are greater than 17.

In our second example we create an array (arrDates) of Date objects, then use filter to create a new array (arrDatesLaterThan) of only those dates from arrDates which are later than 1/1/1990.

The third example tests each element of the array arrPossibleSquares to see if the element is a perfect square, populating array arrPerfectSquares with those element for which callback function isPerfectSquare returns true.

In the last example we demonstrate the use of the optional thisArg parameter. Recall that the value of this in any JavaScript function is, by default, the global window object. By passing the original array (arr) as the thisArg parameter in the call arr.filter(isUnique, arr), we give callback function isUnique a way to access the original array. Function isUnique counts the number of occurrences of its parameter val, returning true if and only if there is exactly one occurrence. Thus our array uniqueElements contains only the elements from arr which occur exactly once.

Array.prototype.find()

The find method returns the first element from the array for which the callback function returns true; if no such element exists, then find returns undefined:

Array.prototype.find(callbackFunction, thisArgument?)

The method takes an optional second parameter to use as this when executing callbackFunction.

In the next set of examples, we demonstrate how to use the find method with an array of objects:

Code Sample:

AdvancedArrays/Demos/find.html
<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title>Array.prototype.fin</title>
</head>
<body>
	<h1>Array.prototype.find</h1>
	<script>
		var people = [
			{
				fname:'Maria',
				lname:'Martinez',
				id:14637,
				hireDate:new Date(1999,2,4)
			},
			{
				fname:'Jane',
				lname:'Doe',
				id:29384,
				hireDate:new Date(2003,4,13)
			},
			{
				fname:'John',
				lname:'Doe',
				id:39922,
				hireDate:new Date(2004,11,20)
			},
			{
				fname:'Mary',
				lname:'Chan',
				id:83736,
				hireDate:new Date(2014,7,4)
			}
		];

		function hiredAfter2000(person) {
			var jan012000 = new Date(2000,1,1);
			return person.hireDate >= jan012000;
		}

		var firstPersonHiredAfter2000 = people.find(hiredAfter2000);
		document.write('The first person hired after 1/1/2000 is ' + firstPersonHiredAfter2000.fname + ' ' + firstPersonHiredAfter2000.lname);


		document.write('<hr>');
		

		function hasId(person, id) {
			return person.id === id;
		}
		var idToFind = 83736;
		var person = people.find(function(p) {
			return hasId(p, idToFind);
		});
		if (typeof person != 'undefined') {
			document.write(person.fname + " " + person.lname + " has id " + idToFind);
		} else {
			document.write("person not found");
		}

		document.write('<hr>');

		var idToFind2 = 11111;
		var person2 = people.find(function(p) {
			return hasId(p, idToFind2);
		});
		if (typeof person2 != 'undefined') {
			document.write(person2.fname + " " + person2.lname + " has id " + idToFind2);
		} else {
			document.write("person not found");
		}

	</script>
</body>
</html>

Code Explanation

We create an array people where each of the four elements is an object with fields for first name, last name, ID, and date of hire; the array is sorted by hire date, with earlier hires first.

We use the find method with callback function hiredAfter2000 to create firstPersonHiredAfter2000; since the second element of our array is the first element with a hire date later than 1/1/2000, the "Jane Doe" record is the element found and written to the screen.

In the latter two examples we search for the person-object with a specific ID, calling the function hasId in our find method. On line 33 we get the "Mary Chan" element (which has an id of 83736).

On line 45 our find call returns no array element (there is no person with id 11111) and, thus, our test typeof person2 != 'undefined' fails and person not found is written to the screen.

Array.prototype.forEach()

The array method forEach gives us a way to apply a callback function to each element of the array:

Array.prototype.forEach(callbackFunction, thisArgument?)

The method takes an optional second parameter - the value to use as this for the callback function.

The callback function specified by the callbackFunction parameter takes three arguments: the current element, the index of the current element, and the array to which forEach is being applied.

Note that there is no way to break out of a forEach loop other than by throwing a JavaScript exception; use a for loop or similar construct if this behavior is needed.

The following example shows the use of the forEach method:

Code Sample:

AdvancedArrays/Demos/forEach.html
<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title>Array.prototype.forEach</title>
</head>
<body>
	<h1>Array.prototype.forEach</h1>
	<script>
		var nums = [2, 33, 92, 1, 20, 57, 41, 7];

		function writeInfoAsListItem(value, i, arr) {
			document.write('<li>');
			document.write(value + ' at position ' + i + (i < arr.length/2 ? ' (1st half)' : ' (2nd half)'));
			document.write('</li>');
		}

		document.write('<ul>');
		nums.forEach(writeInfoAsListItem);
		document.write('</ul>');
	</script>
</body>
</html>

Code Explanation

We create an eight-element array of numbers, nums, and call forEach on the array on line 19. Our callback function, writeInfoAsListItem, writes to the screen information about each element, formatted as an HTML list item: the value of the element, the index of the element, and whether the element is in the first or second half of the array.

Array.prototype.indexOf()

The indexOf method returns the first index at which the specified element is found in the array to which it is applied, or -1 is the element is not found:

Array.prototype.indexOf(elementToFind, start?)

We can pass an optional parameter to specify the index at which to start the search; passing a start parameter which is greater than or equal to the length of the array returns -1.

In the following example, we use indexOf to search an array and to build a function which returns the unduplicated elements of an array:

Code Sample:

AdvancedArrays/Demos/indexOf.html
<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title>Array.prototype.indexOf</title>
</head>
<body>
	<h1>Array.prototype.indexOf</h1>
	<script>
		var nums = [2, 32, 92, 1, 2, 20, 57, 32, 2, 41, 7];

		document.write(nums.indexOf(32)); // returns 1
		document.write('<hr>');
		document.write(nums.indexOf(92, 5)); // returns -1
		document.write('<hr>');
		document.write(nums.indexOf(547)); // returns -1

		document.write('<hr>');

		function unique(arr) {
			var arrayOfUnique = [];

			arr.forEach(function(currentElement) {
				if (arrayOfUnique.indexOf(currentElement) == -1) {
					arrayOfUnique.push(currentElement);
				}
			});
			
			return arrayOfUnique;
		}

		document.write('Original array: ' + nums);
		document.write('<br>Unique elements: ' + unique(nums));

		

	</script>
</body>
</html>

Code Explanation

nums is an array of integers; note that a few of the elements (2 and 32) are duplicated.

Our call to nums.indexOf(32) on line 12 returns 1, since the first occurrence of 32 in the array comes at index 1.

The call nums.indexOf(92, 5) returns -1 since the value 92 is not found in the array if we start looking from index 5.

The call nums.indexOf(547) returns -1 since the value 547 is not present in the array.

Our function unique returns an array of unduplicated elements: we loop over the array, adding each current element to a new (initially empty) array arrayOfUnique if the current element has not (arrayOfUnqiue.indexOf(currentElement) == -1) already been added to it.

Array.prototype.keys()

The array method keys returns a new Array Iterator with the keys from each index of the array to which it was applied. It does not change the array on which it is called:

Array.prototype.keys()

Note that the keys method does not ignore "holes" in the array, as in the array ['one', , 'three']. The following example shows the use of keys:

Code Sample:

AdvancedArrays/Demos/keys.html
<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title>Array.prototype.keys</title>
</head>
<body>
	<h1>Array.prototype.keys</h1>
	<script>
		var nums = [10, 20, 30, 40];
		iter = nums.keys();
		while (true) {
			current = iter.next();
			if (current.done) {
				break;
			}
			document.write(current.value + '<br>');
		}

		document.write('<hr>');

		var nums2 = [1, , 3];
		iter = nums2.keys();
		while (true) {
			current = iter.next();
			if (current.done) {
				break;
			}
			document.write(current.value + '<br>');
		}
	</script>
</body>
</html>

Code Explanation

In the first example we get the keys from a four-element array as an iterator and use the next() method and done property to loop over the iterator object, writing 0, 1, 2, and 3 (the indices of the four elements) to the screen.

The second example shows that the "missing" second element of array nums2 still results in the values 0, 1, and 2 being written to the screen.

Array.prototype.map()

The map method returns a new array from the application of its callback-function parameter to each element of the original array:

Array.prototype.map(callbackFunction, thisArgument?)

Optionally, we can supply a value for the callback function to use as this.

The callback function is excuted on each element of the array in order and takes three arguments: the current element, the current index, and the original array to which map is being applied.

In the following example, we use the map method to round an array of numbers and to produce an array of full names from an array of objects:

Code Sample:

AdvancedArrays/Demos/map.html
<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title>Array.prototype.map</title>
</head>
<body>
	<h1>Array.prototype.map</h1>
	<script>
		var nums = [1.21, 2.6, 3.3, 4.76];
		var roundedNums = nums.map(Math.round);
		document.write(roundedNums);

		document.write('<hr>');

		var people = [
			{fname:'Marla', lname:'Jayson'},
			{fname:'Jack', lname:'Patel'},
			{fname:'Eric', lname:'Song'},
			{fname:'James', lname:'Doe'}
		];
		function getFullName(person) {
			return person.fname + ' ' + person.lname;
		}
		var peopleFullNames = people.map(getFullName);
		document.write(peopleFullNames);
	</script>
</body>
</html>

Code Explanation

The array nums contains four floating-point values. We create array roundedNums from nums by supplying Math.round as the callback function for map, thus rounding each element of nums to the nearest integer.

The array people contains four objects, each with fields for first name and last name. The callback function getFullName returns the first and last name concatenated with a space; people.map(getFullName) thus produces an array with the full name of each element of the original people array.

Array.prototype.reduce()

The reduce method applies a callback function once for each value of the array, offering a means to return a single, calculated value - the sum of all elements, for example - from the array:

Array.prototype.reduce(callbackFunction, initialValue?)

The callback function executed by reduce takes four arguments:

  • previousVal: The value previously returned by the last invokation of the callback function, or the initalValue parameter, if present.
  • currentVal: The current element being processed.
  • currentInd: The index of the current element being processed.
  • arr: The array upon which reduce was called.

If you apply reduce to an array and do not supply an initialValue parameter, then previousVal will be equal to the first value of the array and currentVal will be equal to the second value of the array.

Let's look at a few examples to better explain the use of reduce:

Code Sample:

AdvancedArrays/Demos/reduce.html
<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title>Array.prototype.reduce</title>
</head>
<body>
	<h1>Array.prototype.reduce</h1>
	<script>
		var nums = [6, 2, 4, 7, 9, 11];
		var total = nums.reduce( (a, b) => a + b );
		document.write(total);

		document.write('<hr>');

		function isPrime(num) {
			if (num < 2) {
				return false;
			}
			var sqroot = Math.sqrt(num);
			for (var i=2; i<=sqroot; i++) {
				if (num % i == 0) {
					return false;
				}
			}
			return true;
		}

		function countPrimes(total, num) {
			if (isPrime(num)) {
				return total + 1;
			}
			return total;
		}

		var numPrimes = nums.reduce(countPrimes, 0);
		document.write(numPrimes);

		document.write('<hr>');

		var average = nums.map( function(value, i, arr) {
			return value / arr.length;
		}).reduce( (a, b) => a + b );

		document.write(average);
	</script>
</body>
</html>

Code Explanation

We demonstrate three uses of reduce, all applied to our six-element array of integers nums.

In the first example on line 11, we find the sum of the elements of the array using a callback function defined as an arrow function (a,b) => a+b. Because we do not supply the optional initialValue parameter in our call to reduce, the first execution of the arrow function takes the first element (6) as previousVal and the second element (2) as currentVal, returning their sum, the value 8. Remember that the arrow function - the callback function supplied to reduce - is executed once per element; the second time it is executed it receives the value returned from the last call (8) as previousVal and the third element (4) as currentVal, returning their sum as 12. This process of generating a running sum continues until all elements have been processed and the total sum, or 39, is returned.

The second example invokes callback function countPrimes, with parameters total and num; these are the previousVal and currentVal parameters, respectively. On line 36 we supply 0 for initialValue; for each iteration of the callback function, countPrimes increments the running count of primes if the current element being processed (its parameter num) is indeed prime - that is, if our utility function isPrime returns true for that value.

In our last example, we chain together the map and reduce methods on our array to get the average (mathmatical mean) of the elements in array num. We use the map method to return a new array which is the result of dividing each element in nums by the length of nums; we then use reduce to find the sum of the elements of that array. Since addition is commutative over division (that is, adding the elements and then dividing by the total number of elements is the same as dividing each element by the total number of elements and adding the results), we get the average of the array.

We'll ask you to try out Array methods in the next exercise:

Find the Mode

Duration: 20 to 30 minutes.

In this exercise, you will use Array methods to find the mode - element(s) occurring most frequently - of an array.

  1. Open AdvancedArrays/Exercises/mode.html for editing.
  2. Assume that the array nums always contains integer elements greater than or equal to 0.
  3. Write method Mode which returns an array of the mode(s) from its integer parameter arr; for example:
    • Mode([1,1,2,1]) would return [1]
    • Mode([0,17,17,9,2,9]) would return [9,17]
    • Mode([1,2]) would return [1,2]
  4. One strategy for finding the mode is:
    • Find the maximum value (max) in the array (hint: use reduce);
    • Create a new array with with length max + 1 to record the frequencies of occurrence of each element;
    • Set the value of each element in the frequency array to 0;
    • Iterate over the original array, incrementing the value of the frequency table whose index is the current value from the original array;
    • Find the maximum value from the frequency table;
    • Return an array containing the indices of all elements in the frequency table whose value is the maximum value.
  5. Test your solution in a browser.

Challenge

Design a Mode method that will work for an array where some elements may be negative.

Solution:

AdvancedArrays/Solutions/mode.html
<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title>Mode</title>
</head>
<body>
	<h1>Mode</h1>
	<script>
		var nums = [92, 2, 33, 92, 33, 7, 20, 20, 20, 33, 57, 92, 41, 7];
		document.write(nums);

		function mode(arr) {
			var max = arr.reduce( (n, m) => n > m ? n : m );
			var frequencies = new Array(max + 1);
			var modes = [];

			frequencies.fill(0);

			arr.forEach(function(value) {
				frequencies[value] = frequencies[value] + 1;
			});

			var maxFrequency = frequencies.reduce( (n, m) => n > m ? n : m );

			var maxOccurringElement = frequencies.indexOf(maxFrequency);
			while (maxOccurringElement != -1) {
				modes.push(maxOccurringElement);
				maxOccurringElement = frequencies.indexOf(maxFrequency, maxOccurringElement + 1);
			}
			return modes;
		}

		document.write('<hr>');
		var mode = mode(nums);
		document.write(mode);
	</script>
</body>
</html>

Code Explanation

The reduce method offers an easy way to find the maximum value in arr: compare each adjacent element in the array and return the greater value; we set local variable max to the greatest value in arr.

We create local array frequencies with length max + 1 and, using the fill method, set all of its values to 0.

We iterate over arr using forEach and increment the element of frequencies whose index correponds to the current element from arr. When forEach finishes, the value of any element in the frequencies array is the frequency of occurrence of its index as an element in array arr.

Again using reduce, we find the maximum value in frequencies and set maxFrequency; this is the number of times that the most-frequently-occurring element(s) were found in arr.

We use indexOf to find all of the indices from the frequencies array whose value is maxFrequency, adding each to the array modes and return modes.

Challenge Solution:

AdvancedArrays/Solutions/mode-challenge.html
<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title>Mode</title>
</head>
<body>
	<h1>Mode</h1>
	<script>
		var nums = [-1, -1, -1, 10, 10, 100, 99, 99, 99];
		document.write(nums);

		function mode(arr) {
			var max = arr.reduce( (n, m) => n > m ? n : m );
			var min = arr.reduce( (n, m) => n < m ? n : m );
			            var conversionValue = min < 0 ? Math.abs(min) : 0;
			            var frequencies = new Array(max + conversionValue + 1);
			var modes = [];

			frequencies.fill(0);

			arr.forEach(function(value) {
				frequencies[value + conversionValue] = frequencies[value + conversionValue] + 1;
			});

			var maxFrequency = frequencies.reduce( (n, m) => n > m ? n : m );

			var maxOccurringElement = frequencies.indexOf(maxFrequency);
			while (maxOccurringElement != -1) {
				modes.push(maxOccurringElement - conversionValue);
				maxOccurringElement = frequencies.indexOf(maxFrequency, maxOccurringElement + 1);
			}
			return modes;
		}

		document.write('<hr>');
		var mode = mode(nums);
		document.write(mode);
	</script>
</body>
</html>

Code Explanation

We adopt the same basic strategy in finding the mode(s) for arrays with negative values, but here we find the minimum value of arr and, if less than 0, set a local variable conversionValue equal to the absolute value of the minimum value.

We use conversionValue to "shift" the frequencies array, recording the frequency of the most-negative (least) value from arr in the 0th element of frequencies.

We record frequencies as before. When building the array (modes) to return as our answer, we "unshift" the indices with modes.push(maxOccurringElement - conversionValue).