如何在JavaScript的.map()函数中改变原始数组?

27
例如:
var persons = [{ "name":"A", "salary":1200 }, { "name":"B", "salary":"1500" }];

你希望修改原始数组中每个人的“薪资”值。


3
除了答案之外: map 的作用非常明显是始终创建一个新数组,详见 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/map 中的“返回值”。这来自于函数式编程的背景,保持原始结构的不可变性是一种特性。在并行系统(如Spark)中,map 用于将工作分配给节点:每个 map 函数都在不同的系统上执行。 - Mörre
你可以使用第二个参数thisArg,但这与forEach是等价的。 - stackoverflow
11个回答

50

如果你想改变原始数组,你可以使用Array#forEach函数。

const persons = [{ "name":"A", "salary":1200 }, { "name":"B", "salary": 1500 }];

persons.forEach(item => item.salary += 1000);

console.log(persons)

Array#map 创建一个新数组并返回创建的项目。之后需要将返回的结果赋值。

let persons = [{ "name":"A", "salary":1200 }, { "name":"B", "salary": 1500 }];

persons = persons.map(item => {
  item.salary += 1000;
  return item;
});

console.log(persons);


“if its items are reference types”是什么意思?无论您在数组中存储什么,相同的逻辑都适用。 - Etheryte
8
如果你有一个由基本类型构成的数组,并且希望在 forEach 循环中更改这些值,那么你将得到它们的副本,实际的数组项不会被改变。 - Suren Srapyan
1
如果在回调函数中添加第二个参数并使用 persons[i] += 10,则可以将 forEach 与原始值一起使用,这与 for 循环相同。 - jcubic
1
在那种情况下,是的,你可以。 - Suren Srapyan
2
对于 map,即使不将结果重新赋值给 persons 变量,并在控制台中打印,与 forEach 给出相同的结果,这可能是因为对象是按引用传递的原因? - Sandeep Kumar
显示剩余3条评论

11

如果我理解正确的话,您可以通过直接迭代 map 来改变对象。

persons.map(i => { i.salary = i.salary * 1.25; return i; });
console.log(persons);
// [{ "name":"A", "salary": 1875 }, { "name":"B", "salary": 2343.75 }]

虽然这是反模式,你应该避免在 map() 中对原始数组进行修改。


2
这个答案完全正确,所以我投了赞成票。为什么要投反对票呢?避免变异是一个选择而不是规则。 - JohnPan
4
使用map函数却不需要其结果被认为是一种反模式。 - acme

3

我看到了其他答案,你可以使用其中任意一种方法,但是我认为有些问题。

我将提到两种我在许多不同语言中使用的方法,mapforEachmap 是一种函数式地遍历集合并创建一些新集合的方法,与语言无关。使用 map,预期要创建一个由初始集合映射而来的新集合。另一方面,forEach 是一种方法,通过不使用通常用于集合的 for 循环语法来简化遍历集合,并在需要时改变每个项目。

如果您在包含对象的集合上使用 map 并在 mapper 函数中更改这些对象,则可能会遇到意外行为。因为您直接更改了正在操作的对象,而没有将其映射到另一个对象。该对象可能被视为一种状态,计算机基于状态转移工作。如果您想更改该对象,即某个状态,那么完全可以,但是根据 说明,不应在此类情况下使用 map。因为您不是创建具有一些新值的新数组,而是改变提供的元素。在这种情况下,请使用 forEach

我在这里添加了一个示例。您可以单击链接并查看控制台,以更清楚地了解我的意思。

据我所知,根据我的经验,在 map 方法中进行更改被认为是不良实践并且不鼓励使用。

这两个方法是为不同的目的添加的,最好按预期使用它们。

有关更多信息,请参见Mozilla Web Docs Array 页面


3
使用 forEach。如果您的数组中充满了对象,并且您只想更改这些对象 - 就像在原始问题中一样 - 那么您可以简单地让回调函数更改传递给它的第一个参数中的项目。
如果您的数组中充满了基元,这些基元只能通过将其他东西赋值给数组槽来进行更改,从而使数组发生变化 - 或者由于某种其他原因,您希望重新分配事物到数组槽中 - 您仍然可以使用 forEach。传递给回调函数的第二个和第三个参数是当前操作的元素的数组索引和数组本身的引用。因此,在回调函数内部,您可以分配到相关的数组槽。

2
您可以使用简单的for循环来实现这个功能。

var persons = [{ "name":"A", "salary":1200 }, { "name":"B", "salary":"1500" }];

for(let element of persons){
    element.salary*=2;
}

console.log(persons);


2

.map()函数在其回调中接受第三个参数,即原始数组的实例。

您也可以这样做:

var persons = [{ "name":"A", "salary":1200 }, { "name":"B", "salary":1500 }];

persons.map(function(person, key, array) {
  array[key].salary *= 2;
});

console.log(persons);


4
不良做法-使用forEach。是的,你可以用reducemap实现使用forEach或for循环可以实现的所有操作,但这并不意味着你应该这样做。即使它们有重叠,也有不同的函数,这是有原因的。阅读代码不应该比本来更难。熟悉不同功能背后深层概念的人会被误导,除非他们特别注意(而不熟悉的人则不会获得任何收益)。有关上下文,请参见我的评论。 - Mörre

2

JavaScript内置了一个数组方法map,它可以遍历数组的值。

persons.map(person => person["salary"] + 1000)

1

var persons = [{ "name":"A", "salary":1200 }, { "name":"B", "salary":"1500" }];

var mutatedPersons = persons.map(function(obj){
   return {name:obj.name,salary:parseInt(obj.salary) + 100};
})

console.log(mutatedPersons);


1
尝试:
let persons = persons.map((person) => {person['salary'] = parseInt(person['salary']) + 1; return person})

parseInt函数可以将薪资字符串转换为整数值。 - Siddhant Shrivastav
1
尽管此代码可能解决了提问者的问题,最好还是解释一下您的代码如何解决他们的问题。这样,未来的访问者可以从您的帖子中学习,并将其应用于自己的代码中。SO不是一个编码服务,而是一个知识资源。此外,高质量、完整的答案更容易获得赞同。这些特点以及所有帖子必须是自包含的要求,是SO作为一个平台的一些优势,这使它与论坛有所区别。您可以编辑以添加其他信息和/或使用源文档补充您的解释。 - ysf

0
如果您有一个基本类型的数组,您可以使用这个函数:
function mapInplace<T>(arr: T[], callback: (v: T, i: number) => T) {
    for(const [i, v] of arr.entries()) {
        arr[i] = callback(v, i)
    }
}

使用示例:

mapInplace(weights, w => w / total)

由于它正在改变数组,因此没有返回值。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接