Learn › JavaScript ES6+

Arrays & Iterators

Master array transformation methods, the for...of loop, and generator functions for custom iteration.

Map, Filter, and Reduce

The map, filter, and reduce methods are the cornerstone of functional array processing in JavaScript. The map method transforms each element by applying a function and returns a new array of the same length. Filter creates a new array containing only elements that pass a test function. Reduce processes all elements into a single accumulated value by applying a reducer function from left to right. These methods do not modify the original array and can be chained together for complex data transformations.

const products = [
  { name: "Laptop", price: 999, category: "electronics" },
  { name: "Shirt", price: 29, category: "clothing" },
  { name: "Phone", price: 699, category: "electronics" },
  { name: "Pants", price: 49, category: "clothing" },
  { name: "Tablet", price: 449, category: "electronics" },
];

// Map - transform elements
const names = products.map((p) => p.name);
console.log(names); // ["Laptop", "Shirt", "Phone", "Pants", "Tablet"]

// Filter - select elements
const electronics = products.filter((p) => p.category === "electronics");
console.log(electronics.length); // 3

// Reduce - accumulate a value
const total = products.reduce((sum, p) => sum + p.price, 0);
console.log("Total:", total); // 2225

// Chaining methods
const avgElectronicsPrice = products
  .filter((p) => p.category === "electronics")
  .map((p) => p.price)
  .reduce((sum, price, _, arr) => sum + price / arr.length, 0);
console.log("Avg electronics:", avgElectronicsPrice); // 715.67

// Reduce to group by category
const grouped = products.reduce((groups, p) => {
  const key = p.category;
  groups[key] = groups[key] || [];
  groups[key].push(p);
  return groups;
}, {});
console.log(grouped);

Find and Other Array Methods

ES6 introduced several utility methods that simplify common array operations. The find method returns the first element that satisfies a test function, while findIndex returns its index. The includes method checks if an array contains a specific value, replacing the old indexOf !== -1 pattern. The some and every methods test whether any or all elements pass a condition. Array.from converts array-like objects and iterables into real arrays.

const users = [
  { id: 1, name: "Alice", role: "admin" },
  { id: 2, name: "Bob", role: "user" },
  { id: 3, name: "Charlie", role: "user" },
  { id: 4, name: "Diana", role: "admin" },
];

// find - first match
const admin = users.find((u) => u.role === "admin");
console.log(admin); // { id: 1, name: "Alice", role: "admin" }

// findIndex - index of first match
const bobIndex = users.findIndex((u) => u.name === "Bob");
console.log(bobIndex); // 1

// includes
const numbers = [1, 2, 3, 4, 5];
console.log(numbers.includes(3)); // true
console.log(numbers.includes(6)); // false

// some - at least one matches
const hasAdmin = users.some((u) => u.role === "admin");
console.log("Has admin:", hasAdmin); // true

// every - all match
const allUsers = users.every((u) => u.role === "user");
console.log("All users:", allUsers); // false

// Array.from
const nodeList = "Hello";
const chars = Array.from(nodeList);
console.log(chars); // ["H", "e", "l", "l", "o"]

// Array.from with map function
const squares = Array.from({ length: 5 }, (_, i) => (i + 1) ** 2);
console.log(squares); // [1, 4, 9, 16, 25]

for...of Loop

The for...of loop provides a clean syntax for iterating over any iterable object, including arrays, strings, Maps, Sets, and generators. Unlike for...in which iterates over property keys (and can include inherited properties), for...of iterates over values. It supports break and continue for early termination and skipping, which is not possible with forEach. The for...of loop works with any object that implements the Symbol.iterator protocol.

// Iterating arrays
const colors = ["red", "green", "blue"];
for (const color of colors) {
  console.log(color);
}

// Iterating strings (Unicode-aware)
for (const char of "Hello") {
  console.log(char);
}

// Iterating Maps
const userRoles = new Map([
  ["Alice", "admin"],
  ["Bob", "editor"],
  ["Charlie", "viewer"],
]);

for (const [name, role] of userRoles) {
  console.log(`${name}: ${role}`);
}

// Iterating Sets
const uniqueNumbers = new Set([1, 2, 3, 2, 1]);
for (const num of uniqueNumbers) {
  console.log(num); // 1, 2, 3
}

// With entries() for index
const fruits = ["apple", "banana", "cherry"];
for (const [index, fruit] of fruits.entries()) {
  console.log(`${index}: ${fruit}`);
}

// Break and continue work naturally
const data = [1, -2, 3, -4, 5];
for (const val of data) {
  if (val < 0) continue;
  if (val > 3) break;
  console.log(val); // 1, 3
}

Generators

Generator functions use the function* syntax and can pause and resume their execution using the yield keyword. When called, a generator function returns a generator object that conforms to both the iterable and iterator protocols. Each call to next() resumes execution until the next yield expression, returning an object with value and done properties. Generators are powerful for creating custom iterators, handling infinite sequences, and implementing lazy evaluation.

// Basic generator
function* count(start, end) {
  for (let i = start; i <= end; i++) {
    yield i;
  }
}

for (const n of count(1, 5)) {
  console.log(n); // 1, 2, 3, 4, 5
}

// Infinite sequence
function* fibonacci() {
  let a = 0;
  let b = 1;
  while (true) {
    yield a;
    [a, b] = [b, a + b];
  }
}

// Take first N values from a generator
function take(n, iterable) {
  const result = [];
  for (const value of iterable) {
    result.push(value);
    if (result.length >= n) break;
  }
  return result;
}

console.log(take(8, fibonacci())); // [0, 1, 1, 2, 3, 5, 8, 13]

// Generator with values passed in
function* accumulator() {
  let total = 0;
  while (true) {
    const value = yield total;
    if (value === null) return total;
    total += value;
  }
}

const acc = accumulator();
acc.next();        // { value: 0, done: false }
acc.next(10);      // { value: 10, done: false }
acc.next(20);      // { value: 30, done: false }
console.log(acc.next(null)); // { value: 30, done: true }

← Objects & Prototypes · Destructuring & Spread →