Marcy Lab School Docs
  • Welcome
  • Student Guidelines & Policies
    • Student Handbook
    • AI Policy
    • Academic Calendar
  • Environment Setup
    • Local Environment Setup - Mac
    • Local Environment Setup - Windows
    • GitHub Setup
    • Postgres Setup
  • How-Tos
    • How To Code at Marcy: Code Style Guide
    • How to Do Short Response and Coding Assignments
    • How to Debug
    • How to PEDAC
    • How to Create A GitHub Organization and Scrumboard
    • How to Create Projects with Vite
    • How to Deploy on GitHub Pages
    • How to Deploy on Render
    • How to Test your API with Postman
  • Mod 0 - Command Line Interfaces, Git, and GitHub
    • Overview
    • 1. Command Line Interfaces
    • 2. Git & GitHub
    • 3. Git Pulling & Merging
    • 4. Git Branching & PRs
  • Mod 1 - JavaScript Fundamentals
    • Overview
    • 1. Intro to Programming
    • 2. Errors
    • 3. Node & Node Modules
    • 4. Variables, Functions & String Methods
    • 5. Control Flow, typeof, and Math
    • 6. Loops
    • 7. Arrays
    • 8. Objects
    • 9. Higher Order Functions: Callbacks
    • 10. Higher Order Functions: Array Methods
    • 11. Regex
  • Mod 2 - HTML, CSS & the DOM
    • Overview
    • 1. HTML
    • 2. CSS
    • 3. Accessibility (a11y)
    • 4. The Document Object Model (DOM) API
    • 5. Events
    • 6. Forms
    • 7. The Box Model and Positioning
    • 8. Flexbox
    • 9. Grid & Media Queries
    • 10. ESModules
    • 11. Vite
    • 12. LocalStorage
  • Mod 3 - Async & APIs
    • Overview
    • 1. Promises
    • 2. Fetch
    • 3. Building a Fetching App
    • 4. Async & Await
    • 5. A Generic Fetch Handler
  • Mod 4 - Project Week!
    • Important How Tos and Guides
      • How to Create a GitHub Organization and Scrum Board
      • How To Start a Project with Vite
      • How To Deploy a Project with GitHub Pages
    • Project Week Overview
    • Agile Methodologies
    • Deliverables & Milestones
    • Technical Requirements Checklist
    • Free API List
    • Collaborative GitHub
  • Mod 5 - Object-Oriented Programming
    • Overview
    • 1. Intro to OOP, Encapsulation, Factory Functions, and Closure
    • 2. Classes
    • 3. Private & Static
    • 4. UML Diagrams & Has Many/Belongs To Relationships
    • 5. Challenge: Implementing Has Many/Belongs To
    • 6. Inheritance
    • 7. Polymorphism
    • 8. Review and Practice
    • MDN: Object Prototypes
  • Mod 6 - Data Structures & Algorithms
    • Overview
    • Important How Tos and Guides
      • How to Debug
      • How to PEDAC
    • 1. Nodes & Linked Lists
    • 2. Singly & Doubly Linked Lists
    • 3. Stacks & Queues
    • 4. Recursion
    • 5. Trees
  • Mod 7 - React
    • Overview
    • Important How Tos and Guides
      • How to Create Projects with Vite
      • How to Deploy on GitHub Pages
    • 1. Intro to React
    • 2. Events, State, and Forms
    • 3. Fetching with useEffect
    • 4. React Router
    • 5. Building a Flashcards App
    • 6. React Context
    • 7. Global Context Pattern
  • Mod 8 - Backend
    • Overview
    • Important How Tos and Guides
      • How to Deploy on Render
      • How to Test your API with Postman
      • Postgres Setup
    • 1. Intro to Express
    • 2. Building a Static Web Server with Middleware
    • 3. Securing API Keys and Environment Variables
    • 4. RESTful CRUD API
    • 5. Model-View-Controller Architecture
    • 6. SQL and Databases
    • 7. JOIN (Association) SQL Queries
    • 8. Knex
    • 9. Your First Fullstack App!
    • 10. Migrations & Seeds
    • 11. Schema Design & Normalization
    • 12. Hashing Passwords with Bcrypt
    • 13. React Express Auth Template Overview
  • Mod 9 - Civic Tech Hackathon
    • Overview
    • Rubric
  • Mod 10 - Capstone
    • Overview
Powered by GitBook
On this page
  • Imperative vs. Declarative
  • Array Iterators: forEach, filter, and map
  • forEach
  • filter
  • map
  • Array Higher Order Methods
  • .forEach(callback)
  • .filter(testCallback)
  • .map(modifyCallback)
  • .find(testCallback)
  • .findIndex(testCallback)
  • .reduce(accumulatorCallback, startingValue)
  • Advanced Stuff
  1. Mod 1 - JavaScript Fundamentals

10. Higher Order Functions: Array Methods

Previous9. Higher Order Functions: CallbacksNext11. Regex

Last updated 7 months ago

Follow along with code examples !

Table of Contents

Imperative vs. Declarative

Imperative code provides explicit instructions for exactly HOW to complete a task.

  • High control (you write every single line)

  • High effort (you write every single line)

// Double every number in the given array
const doubleAllNums = (arr) => {
  const newArr = [];
  for (let i = 0; i < arr.length; i++) {
    const result = arr[i] * 2
    newArr.push(result)
  }
  return newArr;
}
const doubledNumsOld = doubleAllNums([1, 5, 10, 20]);
console.log(doubledNumsOld);

Declarative code provides the desired solution without specifying HOW to get there.

  • Low control

  • Low effort

const doubledNums = [1, 5, 10, 20].map((num) => num * 2);

Array Iterators: forEach, filter, and map

The most commonly used and perhaps most powerful higher-order functions are these "iterator" array methods:

  • arr.forEach — useful for performing some repetitive task for each value in the source array

  • arr.filter — useful for testing every value in the source array and keeping only the values that pass the test (e.g. only the even numbers in a numbers array)

  • arr.map — useful for transforming every value in the source array into another value (e.g. capitalize every string in an array of sentences);

Let's take a look at each of them. We can see how they are used and how we can create our own versions of them.

forEach

arr.forEach(callback) iterates over the source array and invokes the given callback with each value, that value's index, and the source array arr as inputs.

Use forEach to execute callbacks that produce side-effects (like printing to the console or mutating values).

Examples:

In this example, we want to print a message about each value and its location in the fruits array:

0-forEach.js
// print every fruit loudly!
const fruits = ['apples', 'bananas', 'cherries'];
const printLoudly = (input, index, arr) => {
  console.log(`${input}!!! at index ${index} in array [${arr}]`);
}
fruits.forEach(printLoudly);

// print each fruit value capitalized
fruits.forEach(value => {
  console.log(value.toUpperCase())
});

In this example, we provide an anonymous inline function. We have chosen to ignore the index and source array inputs and just focus on each user value in the users array:

0-forEach.js
// revoke isAdmin status from all users
const users = [
  { id: 1, username: 'ben', isAdmin: false },
  { id: 2, username: 'maya', isAdmin: true },
  { id: 3, username: 'reuben', isAdmin: true },
  { id: 4, username: 'gonzalo', isAdmin: false },
];
users.forEach(user => user.isAdmin = false)

Challenge:

Implement your own forEach function that takes an array and a callback and invokes the callback on every value in the array. The callback should be invoked with 3 inputs for every value:

  1. The current value

  2. The current value's index in the source array

  3. The source array itself

Nothing is returned.

const forEach = (array, callback) => {
  // ??? 
};

forEach(['a','b','c'], console.log);
/* 
a 0 [ 'a', 'b', 'c' ]
b 1 [ 'a', 'b', 'c' ]
c 2 [ 'a', 'b', 'c' ]
*/
Solution
const forEach = (array, callback) => {
  for (let i = 0; i < array.length; i++) {
    callback(array[i], i, array);
  }
}

filter

arr.filter(callback) iterates over the source array, invoking the given callback with each value, index, and the source array arr as inputs. If the callback returns true, the value is added to an array which is returned.

Use filter when you want a copy of the source array with unwanted values removed.

Examples:

In this example, we want to know how many scores in an array of numbers are greater than or equal to 75. Using arr.filter, we can get a copy of the array containing only those passing scores and then read its length.

1-filter.js
const scores = [100, 85, 90, 70, 74];

// We only care about the value, the index and arr can be ignored
const isPassing = (score) => {
  return score >= 75;
}

// This is a copy of scores that has been filtered.
const passingScores = scores.filter(isPassing);

// Using its length, we can get the number of passing scores
console.log(`There were ${passingScores.length} passing scores`);

In this example, we will have an array of user objects. We want to get a copy of the array that only contains admins. Filter lets us check each object and only those with isAdmin: true will pass the test and be returned in the new array.

1-filter.js
const users = [
  { id: 1, username: 'ben', isAdmin: false },
  { id: 2, username: 'maya', isAdmin: true },
  { id: 3, username: 'reuben', isAdmin: true },
  { id: 4, username: 'gonzalo', isAdmin: false },
];

const admins = users.filter(user => user.isAdmin);

console.log(admins);
/* 
[
  { id: 2, username: 'maya', isAdmin: true },
  { id: 3, username: 'reuben', isAdmin: true },
];
*/

Challenge:

Implement your own filter function that takes an array and a test callback and returns an array. The test callback should be invoked with 3 inputs for every value:

  1. The current value

  2. The current value's index in the source array

  3. The source array itself

The test callback should be expected to return true or false. If test returns true, the value it was called on should be added to the returned array.

const filter = (array, test) => {
  // ??? 
};

const nums = [1, 2, 3, 4, 5];
const evens = filter(nums, value => value % 2 === 0);
console.log(evens);
// [2, 4]
Solution
const filter = (array, test) => {
  // Create a new array to be returned
  const filtered = [];
  for (let i = 0; i < array.length; i++) {
    // If array[i] passes the test, add it to filtered
    if (test(array[i], i, array)) {
      filtered.push(array[i]);
    }
    // Otherwise, continue
  }
  // Return the filtered values only.
  return filtered;
}

map

arr.map(callback) iterates over the source array, invoking the given callback with each value, index, and the source array arr as inputs. The value returned by the callback is added to an array which is returned.

Use map when you want a copy of the source array with each value converted to a new value.

Examples:

In this example, we have an array inchesArr containing values each representing a number of inches. Using arr.map, we transform each inches value into a string in the format x inches => y feet z inches. Each of these new strings is added to the array returned by map and stored in feetAndInches:

2-map.js
const inchesArr = [70, 55, 62, 12, 36];

// This example converts inches to feet and inches strings
const getFeetAndInchesStr = inches => {
  const feet = Math.floor(inches / 12);
  const remainingInches = inches % 12;
  return `${inches} inches => ${feet} feet ${remainingInches} inches`;
}
const feetAndInches = inchesArr.map(getFeetAndInchesStr);
console.log(feetAndInches);

In this example, we take a users array full of user objects and extract all of the usernames into an array:

2-map.js
const users = [
  { id: 1, username: 'ben', isAdmin: false },
  { id: 2, username: 'maya', isAdmin: true },
  { id: 3, username: 'reuben', isAdmin: true },
  { id: 4, username: 'gonzalo', isAdmin: false },
];
const usernames = users.map(user => user.username);

Challenge:

Implement your own map function that takes an array and a modify callback and returns an array. The modify callback should be invoked with 3 inputs for every value:

  1. The current value

  2. The current value's index in the source array

  3. The source array itself

The modify callback should be expected to return a value that should be added to the returned array.

const map = (array, modify) => {
  // ??? 
};

const nums = [1,2,3,4,5]
const doubled = map(nums, value => value * 2);
console.log(doubled);
// [2,4,6,8,10]
Solution
const map = (array, modify) => {
  const mapped = [];
  for (let i = 0; i < array.length; i++) {
    const result = modify(array[i], i, array);
    mapped.push(result);
  }
  return mapped;
}

Array Higher Order Methods

  • The important ones to know are:

    • .forEach(callback)

    • .map(modify)

    • .filter(test)

    • .find(test)

    • .findIndex(test)

    • .reduce(accumulator, startingValue)

  • Invoke these directly ON the Array:

const nums = [1,2,3];
const doubleNums = nums.map((num) => num * 2);
  • These are Higher Order Methods because they are functions stored within an Array "Object", not as a stand-alone function.

.forEach(callback)

Iterate through an array, but don't make a copy

Callback Should Accept
Callback Should Return
.forEach returns

The current value. Its index and the entire array are optional

nothing

undefined

const letters = ['a', 'b', 'c'];
const callback = (val, idx, arr) => {
  console.log('Value at index:', val);
  console.log('Current index:', idx);
  console.log('Original array:', arr);
};
letters.forEach(callback);
// Value at index: a
// Current index: 0
// Original array: [ 'a', 'b', 'c' ]
// Value at index: b
// Current index: 1
// Original array: [ 'a', 'b', 'c' ]
// Value at index: c
// Current index: 2
// Original array: [ 'a', 'b', 'c' ]

.filter(testCallback)

Get ALL elements in an Array that pass a given test

Callback Should Accept
Callback Should Return
.filter returns

The current value. Its index and the entire array are optional

a boolean used to determine if the current element is kept

a new array of matches (or an empty one)

const numbers = [1, 2, 3, 4, 5, 6, 7, 8];

const evenNumbers = numbers.filter((num) => !(num % 2));
console.log('evenNumbers', evenNumbers);
// evenNumbers [ 2, 4, 6, 8 ]

const greaterThan100 = numbers.filter((num) => num > 100);
console.log('greaterThan100', greaterThan100);
// greaterThan100 []

.map(modifyCallback)

Make a copy of an array with changes to each value

Callback Should Accept
Callback Should Return
.map returns

The current value. Its index and the entire array are optional

modified version of the current value

the new array with all the changes

const numbers = [10, 20, 30];
const bigNumbers = numbers.map((val, idx, arr) => {
  console.log('cb: val, idx, arr', val, idx, arr);
  return val * 100;
});
// cb: val, idx, arr 10 0 [ 10, 20, 30 ]
// cb: val, idx, arr 20 1 [ 10, 20, 30 ]
// cb: val, idx, arr 30 2 [ 10, 20, 30 ]

console.log('bigNumbers: ', bigNumbers); // [1000, 2000, 3000]

.find(testCallback)

Get the first matching element in an Array, or null

Callback Should Accept
Callback Should Return
.find returns

The current value. Its index and the entire array are optional

a boolean used to determine if the match is found or not

the found value or null

const myNums = [2, 4, 7, 5];
const firstOddValue = myNums.find((num) => num % 2 === 0);
console.log('firstOddValue', firstOddValue);
// firstOddValue 7

Remember, the return value of the callback must return a boolean!

.findIndex(testCallback)

Get the index of the first matching element in an Array, or null

Callback Should Accept
Callback Should Return
.findIndex returns

The current value. Its index and the entire array are optional

a boolean used to determine if the match is found or not

the found index or -1

const firstOddValueIdx = myNums.findIndex((num) => num % 2);
console.log('firstOddValueIdx', firstOddValueIdx);
// firstOddValueIdx 2

.reduce(accumulatorCallback, startingValue)

"reduce" array values into a single value.

Callback Should Accept
Callback Should Return
.reduce returns

An accumulator and the current value

the next value of the accumulator (the eventual return value)

the final accumulated value

const lunchCosts = [5, 10, 7, 9, 15, 8, 12];
const startingVal = 0;
const addUpCosts = (accumulator, currentVal) => accumulator + currentVal;

const totalCost = lunchCosts.reduce(addUpCosts, startingVal);
console.log('totalCost', totalCost);
// totalCost 66

// reduce is tricky, always use good names!
const addUpCostsBetter = (total, dailyExpense) => total + dailyExpense;

Advanced Stuff

// Don't forget about more arguments!
const multipliedByIndexAndLength = [1, 2, 3, 4, 5]
  .map((num, idx, arr) => num * idx * arr.length);

// chaining
const myNums = [1, 2, 3, 4, 5];
const numValuesBiggerThan12WhenTripled = myNums
  .map((num) => num * 3)
  .filter((num) => num > 12)
  .length

console.log(numValuesBiggerThan12WhenTripled);
// 1

// advanced Reduce example
const repeaters = [1, 2, 4, 2, 3, 1, 4, 6, 2];
const frequencyCounter = (acc, num) => {
  acc[num] = (acc[num] || 0) + 1;
  return acc;
};

const frequencies = repeaters.reduce(frequencyCounter, {});
console.log('frequencies', frequencies);
// frequencies { '1': 2, '2': 3, '3': 1, '4': 2, '6': 1 }

// more HOMs
const hasAtLeastOneEven = myNums.some((num) => !(num % 2));
console.log('hasAtLeastOneEven', hasAtLeastOneEven);
// hasAtLeastOneEven true
here
Imperative vs. Declarative
Array Iterators: forEach, filter, and map
forEach
filter
map
Array Higher Order Methods
.forEach(callback)
.filter(testCallback)
.map(modifyCallback)
.find(testCallback)
.findIndex(testCallback)
.reduce(accumulatorCallback, startingValue)
Advanced Stuff