成为算法专家必须了解的 Map、Filter、Reduce 和其他数组迭代器

2025-05-28

成为算法专家必须了解的 Map、Filter、Reduce 和其他数组迭代器

在本文中,我们将仔细研究一些数组迭代器,例如mapfilterreduce或其他使用回调的方法,它们使数组更加灵活,并帮助您解决 JavaScript 中的问题和算法。
学习这些高阶函数是编写简洁函数式代码的重要一步,并开启了通往函数式和响应式编程等强大技术的大门。

1) 使用forEach()循环数组for 循环
的一个绝佳替代方案无疑是forEach方法。它循环遍历数组并使用每个值作为参数调用回调函数。回调函数接受 3 个不同的参数:数组中的值、当前索引以及调用回调函数的数组。此外,还有第四个参数,用于在执行回调时用作this(如果未提供,则为undefined)。 注意:forEach()不会改变调用它的数组。


const cities = ['ROME', 'PARIS', 'LONDON', 'LOS ANGELES', 'VIENNA'];

cities.forEach((value, index, array) => {
  console.log(`${index + 1} ${value}`); //output: 1 ROME, 2 PARIS, 3 LONDON, 4 LOS ANGELES, 5 VIENNA
});

//we can use it to invert the "cities" array...even though with reverse() would be better 😉
const invertedCities = [];
cities.forEach((value, index, array) => invertedCities.unshift(value));
console.log(invertedCities); //output: ["VIENNA", "LOS ANGELES", "LONDON", "PARIS", "ROME"]

2) 使用map()创建一个新数组,其中包含对原始数组的每个元素调用的函数的结果;与forEach()
方法 非常相似。它遍历一个数组,并将回调函数作为参数,该函数在原始数组中的每个元素上调用。不同之处在于,它返回一个新数组,该数组中的每个值都替换为回调函数的返回值。


//Let's create a new array with all our numbers squared
const numbers = [1, 2, 3, 4, 5];

const squaredNumbers = numbers.map(number => number * number);
console.log(squaredNumbers); //output: [1, 4, 9, 16, 25]

//We all know which is the most beautiful city in the World... 😉
const cities = ['ROME', 'PARIS', 'LONDON', 'LOS ANGELES', 'VIENNA'];
const bestCity = cities.map(city => (city === 'ROME' ? city : 'ROME'));
console.log(bestCity); //output: ["ROME", "ROME", "ROME", "ROME", "ROME"]

//Let's create an array of HTML tag
const html = cities.map(city => `<li>${city}</li>`);
console.log(html); //output: ["<li>ROME</li>", "<li>PARIS</li>", "<li>LONDON</li>", "<li>LOS ANGELES</li>", "<li>VIENNA</li>"]

//Transform an array of strings in an array of objects
const metalBands = ['IRON MAIDEN', 'SLAYER', 'JUDAS PRIEST'];
const collection = metalBands.map((band, index) => {
  let obj = {}; //create an empty object at any call of the loop
  obj.id = index; //create a key called "id" and set it equal to our index parameter
  obj.band = band; //create a key called "band" and set it equal to our band parameter
  return obj; //return an object at any call with key/value pairs like this: {id: 'index', band: 'band-name'}
});
console.log(collection); //output: [{id: 0, band: "IRON MAIDEN"},{id: 1, band: "SLAYER"}, {id: 2, band: "JUDAS PRIEST"}]

3) 使用filter()过滤数组的值
它返回一个新数组,该数组仅包含原始数组中传递给回调时返回 true 的项目。


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

//Return an array of even values from numbers
const evens = numbers.filter(num => num % 2 === 0);
console.log(evens); //output: [2, 4, 6, 8, 10]

//Return an array of odd values from numbers
const odds = numbers.filter(num => num % 2 !== 0);
console.log(odds); //output: [1, 3, 5, 7, 9]

//All the roads lead to Rome 🚋 ...
const cities = ['ROME', 'PARIS', 'LONDON', 'LOS ANGELES', 'VIENNA'];
const rome = cities.filter(city => city == 'ROME');
console.log(rome); //output: ["ROME"]

//You can chain together this methods 🚀🚀🚀
//Example: let's square all even numbers
const squaredEvens = numbers.filter(num => num % 2 === 0).map(num => num * num);
console.log(squaredEvens); //output: [4, 16, 36, 64, 100];

4) 使用reduce()将每个结果累加合并,返回单个值
真正的魔法就在这里!reduce()是函数式编程的“基石”,如果你掌握了它,你就走上了成为 JS 巫师的正确道路 🤘😉🤘
它是另一种遍历数组中每个项目的方法,但这次它将每个结果累加合并,返回单个值。
回调函数用于描述如何将数组中的每个值与运行总数合并。这通常用于计算存储在数组中的数据。Reducer
函数接受四个参数:累加器、当前值、当前索引和源数组。它还接受一个初始值,用作回调函数首次调用的第一个参数。


const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
//Let's start with a basic example
//Sum all the numbers in an array of integers
const sum = numbers.reduce((accumulator, currentValue, currentIndex, array) => {
  //Look we set the Initial Value to 0, because it is a sum so the count starts at 0
  //So our Accumulator is 0 at first call and we sum it with the Current Value that is 1 at first call...
  //the new Accumulator will be 0 + 1 = 1 ...at any call the Current Value will be added to it
  //till the end of the array
  return accumulator + currentValue;
}, 0);
console.log(sum); // output: 55

//Same example setting the Initial Value to 1 instead of 0 will return ... 56
const anotherSum = numbers.reduce((accumulator, currentValue, currentIndex, array) => {
  return accumulator + currentValue;
}, 1);
console.log(anotherSum); // output: 56

//Sum from an array of objects
const integers = [{ x: 1 }, { x: 2 }, { x: 3 }];
const anotherSumAgain = integers.reduce((acc, val, idx, array) => {
  return acc + val.x;
}, 0);
console.log(anotherSumAgain); // output: 6

//Count vowels in a string (even though it's easier with regex 😉)
const maryPoppins = 'supercalifragilisticexpialidocious';
const onlyVowels = maryPoppins.replace(/[^aeiou]/gi, ''); //'ueaiaiiieiaioiou'
const arrOfVowels = [...onlyVowels]; //["u", "e", "a", "i", "a", "i", "i", "i", "e", "i", "a", "i", "o", "i", "o", "u"]
const countVowels = arrOfVowels.reduce((acc, val) => {
  acc.hasOwnProperty(val) ? (acc[val] += 1) : (acc[val] = 0);
  return acc;
}, {});
console.log(countVowels); // output: {u: 1, e: 1, a: 2, i: 6, o: 1}

//Flatten an array of arrays
//Hey! I know ES2019 gave us flat and flatMap methods, but we MUST learn reduce() now 😉
const someData = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
const flatten = someData.reduce((acc, val) => {
  //set the initial value to an empty array
  return acc.concat(val);
}, []);
console.log(flatten); // output: [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]

//Sum all countries population except China
const population = [
  {
    country: 'China',
    pop: 1409517397
  },
  {
    country: 'India',
    pop: 1339180127
  },
  {
    country: 'USA',
    pop: 324459463
  },
  {
    country: 'Indonesia',
    pop: 263991379
  }
];
const sumPopulationButNotChina = population.reduce((acc, val) => {
  // we use the Ternary Operator as a "filter"
  //if val.country is not equal to "China" we add the value to the accumulator
  //if it is equal to "China" we add nothing and simply return the accumulator
  return val.country !== 'China' ? acc + val.pop : acc;
}, 0);

console.log(sumPopulationButNotChina); // output: 1927630969

reduce()类似的是reduceRight。它对一个累加器和数组中的每个值应用一个函数,但顺序是从右到左。这里就不赘述了,因为这只会重复reduce()中已经讲过的内容。

5) 使用every()检查所有数组项是否通过测试
用于测试的回调函数接受 3 个参数:当前值、索引和数组。返回值为布尔值。如果回调函数对数组中任何元素返回真值,则返回 True。否则返回 false。


//Check if all values are more than zero
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const moreThanZero = numbers.every((val, index, array) => val > 0);
console.log(moreThanZero); //true

const numbersAgain = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const moreThanZeroAgain = numbersAgain.every((val, index, array) => val > 0);
console.log(moreThanZeroAgain); //false

//Check if there are more than 1000000 people in all the countries
const population = [
  {
    country: 'China',
    pop: 1409517397
  },
  {
    country: 'India',
    pop: 1339180127
  },
  {
    country: 'USA',
    pop: 324459463
  },
  {
    country: 'Indonesia',
    pop: 263991379
  }
];

const check = population.every(val => val.pop > 1000000);
console.log(check); //true

6) 使用some()检查数组中的某些项是否通过测试
用于测试的回调函数接受 3 个参数:当前值、索引和数组。返回值为布尔值。如果回调函数为数组中至少一个元素返回真值,则返回 True。否则返回 false。


//Check if a value is more than zero in the array
const numbers = [-1, -2, 0, 10];
const moreThanZero = numbers.some((val, index, array) => val > 0);
console.log(moreThanZero); //true

const numbersAgain = [0, -1, -2];
const moreThanZeroAgain = numbersAgain.some((val, index, array) => val > 0);
console.log(moreThanZeroAgain); //false

//Check if there is at least a country with less than 1000000 people
const population = [
  {
    country: 'China',
    pop: 1409517397
  },
  {
    country: 'India',
    pop: 1339180127
  },
  {
    country: 'USA',
    pop: 324459463
  },
  {
    country: 'Indonesia',
    pop: 263991379
  }
];

const check = population.some(val => val.pop < 1000000);
console.log(check); //false

7) 使用find()查找第一个通过测试的数组项
用于测试的回调函数接受 3 个参数:当前值、索引和数组。如果至少有一个项通过了测试,则返回该项本身。否则返回undefined


//Check if there is a value more than 7
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const moreThanSeven = numbers.find((val, index, array) => val > 7);
console.log(moreThanSeven); //8
//Check if there is a value more than 42
const moreThanFortyTwo = numbers.find((val, index, array) => val > 42);
console.log(moreThanFortyTwo); //undefined

//Check if there is a country with more than 100000000 people
const population = [
  {
    country: 'China',
    pop: 1409517397
  },
  {
    country: 'India',
    pop: 1339180127
  },
  {
    country: 'USA',
    pop: 324459463
  },
  {
    country: 'Indonesia',
    pop: 263991379
  }
];

const check = population.find(val => val.pop > 100000000);
console.log(check); //{ country: 'China', pop: 1409517397 }

这并不是一份详尽的 JavaScript 数组迭代器清单,而是一份我认为在解决问题和算法方面最重要的迭代器清单。
为了提高 JavaScript 水平和解决问题的能力,我建议多尝试所有这些方法,并订阅FreeCodeCampCodewars,在那里你可以找到很多可用的算法,并巩固你的 JavaScript 知识。
Codewars上,你可以找到 7 级或 6 级关于“数组”的算法,并用它们进行训练。这会很有趣也很实用!

如果您对这类文章感兴趣,请看一下这些:
成为算法向导必须知道的 Javascript 字符串方法;
成为算法向导必须知道的 Javascript 数组方法

我会根据大家的反馈和评论,更新这篇文章,提供一些关于数组的新信息和算法。
欢迎关注我的Twitter。

代码长存,繁荣昌盛

文章来源:https://dev.to/uptheirons78/map-filter-reduce-and-others-arrays-iterators-you-must-know-to-become-an-algorithms-wizard-4209
PREV
在 Node.js 中正确使用事件
NEXT
学习 git 概念,而不是命令