当我只是迭代某些东西时,何时使用forEach vs map?

时间:2017-04-11 23:11:35

标签: javascript arrays

我知道map会返回一个新数组,而forEach不返回任何内容(文档说它返回undefined)。

例如,如果我有这样的代码:

let test;

values.forEach((value, idx) => {
  if (someNumber >= value) {
    test = value;
  }
});

我只是检查someNumber是否大于某个值,然后设置test = value。我应该在这里使用另一种数组方法吗?

或者可以使用.forEach

4 个答案:

答案 0 :(得分:3)

您的示例没有意义,因为它找到小于或等于value last someNumber,重复分配给test变量如果找到多个。因此,您的代码并不能真正表达您的意图,因为其他开发人员可能会对您要实现的目标感到困惑。事实上,由于这种模糊性,这里的其他答案对你的目标有不同的看法。你甚至说:

  

如果数字大于或等于数组中任何索引的值,则停止并将test设置为等于该值

但是你的代码并没有停在第一个值!它会持续遍历整个数组,test中的结果将是 last 值,而不是 first 值。

通常,使循环引用外部变量并不是表达意图的最佳方式。这使读者更难理解你在做什么。如果您使用的函数返回一个值,以便明确指定变量,那就更好了。

以下是给您的指南:

<强> forEach

如果要迭代所有值以便对每个值执行某些操作,请使用此选项。如果要创建新的输出值,请不要使用此选项 - 但如果需要修改现有项目或在forEach没有逻辑输出值的每个项目上运行方法,请使用它。 array.forEach at MDN说:

  

除了抛出异常之外,没有办法停止或中断forEach()循环。如果你需要这样的行为,forEach()方法是错误的工具,而是使用普通循环。如果要测试谓词的数组元素并需要布尔返回值,则可以使用every()或some()。如果可用,新方法find()或findIndex()也可用于在真正的谓词上提前终止。

<强> find

当你想要找到某个东西的第一个实例时使用它,然后停止。您所说的让它听起来像你想要的那样:

let testResult = values.find(value => value <= someNumber);

这远远优于在lambda或循环内设置test值。我还认为,由于我们倾向于考虑lambdas的方式,扭转不平等和变量更好。

<强> some

这些只会给你一个布尔值,因此你必须稍微滥用它们来获得输出值。它将遍历数组,直到条件为真或遍历完成,但你必须做一些有点hacky才能获得任何数组值。相反,使用上面的find,它用于输出找到的值而不是简单的true / false,无论条件是否被数组中的任何元素满足。

<强> every

这类似于some,因为它返回Boolean,但正如您所期望的那样,只有当数组中的所有项都满足条件时才会出现。它将遍历数组,直到条件为false或遍历完成。同样,不要通过丢弃布尔结果并将变量设置为值来滥用它。如果您想对数组中的每个项执行某些操作并返回单个值,那么您可能希望使用reduce。另请注意,!arr.every(lambdacondition)arr.some(!lambdacondition)相同。

<强> reduce

实际编写代码的方式 - 查找与条件匹配的最后一个值 - 自然适用于reduce

let testResult = values.reduce(
   (recent, value) => {
      if (value <= someNumber) {
         recent = value;
      }
      return recent;
   },
   undefined
);

这与查找示例代码的最后一个值相同。

<强> map

map用于将数组的每个元素转换为相同长度的新数组。如果您对C#有任何经验,那就像Linq-to-objects .Select方法一样。例如:

let inputs = [ 1, 2, 3, 4];
let doubleInputs = inputs.map(value => value * 2);
// result: [ 2, 4, 6, 8]

新要求

鉴于您在有序数组中找到相邻值的新描述,在这些数组中可以找到某个值,请考虑以下代码:

let sortedBoundaries = [ 10, 20, 30, 40, 50 ];
let inputValue = 37;
let interval = sortedBoundaries
   .map((value, index) => ({ prev: value, next: sortedBoundaries[index + 1] }))
   .find(pair => pair.prev < inputValue && inputValue <= pair.next);
// result: { prev: 20, next: 30 }

你可以改进这一点,以便在数字上使用&gt;也将找到50或&lt; = 10(例如,{ prev: undefined, next: 10 })。

最后的笔记

通过使用这种返回值而不是修改外部变量的编码方式,您不仅可以更好地向其他开发人员传达您的意图,还可以使用const代替let之后不会重新分配变量。

我鼓励您浏览MDN上各种Array原型函数的文档 - 这样做可以帮助您解决这些问题。请注意,我列出的每个方法都是MDN文档的链接。

答案 1 :(得分:2)

我建议您使用Array#some,而不是Array#forEach

Array#forEach 保持迭代数组,即使已满足给定条件也是如此。

Array#some 在满足给定条件时停止迭代。

其中一个优点是与性能相关,另一个优势 - 取决于您的目的 - Array#forEach会在每个传递的条件下覆盖结果,Array#some分配第一个找到的值并停止迭代。 / p>

let test,
    values = [4,5,6,7],
    someNumber = 5;

values.some((value, idx) => {
  if (someNumber >= value) {
    test = value;
    return test;
  }
});

console.log(test);

答案 2 :(得分:2)

另一种选择是使用Array.some()方法。

let test;
const someNumber = 10;

[1, 5, 10, 15].some(function (value) {
  if (value > someNumber) {
    return test = value
  } 
})

.some()方法优于原始解决方案的一个优点是优化,因为一旦满足条件,它将return

答案 3 :(得分:0)

对象怎么样? 你可以搜索for-of