é possível alterar os valores da matriz ao fazer foreach em javascript?

300

exemplo:

var arr = ["one","two","three"];

arr.forEach(function(part){
  part = "four";
  return "four";
})

alert(arr);

A matriz ainda está com seus valores originais; existe alguma maneira de ter acesso de gravação aos elementos da matriz a partir da função de iteração?

rsk82
fonte
1
Relacionados: stackoverflow.com/q/6081868/632951
Pacerier
Tente map ( developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/… ):x=[2,3,4]; x=x.map(n=>n*2); // [4,6,8]
Justin

Respostas:

469

O retorno de chamada é passado para o elemento, o índice e a própria matriz.

arr.forEach(function(part, index, theArray) {
  theArray[index] = "hello world";
});

editar - conforme observado em um comentário, a .forEach()função pode usar um segundo argumento, que será usado como o valor de thisem cada chamada para o retorno de chamada:

arr.forEach(function(part, index) {
  this[index] = "hello world";
}, arr); // use arr as this

Esse segundo exemplo mostra arr-se configurado como thisno retorno de chamada. Alguém pode pensar que a matriz envolvida na .forEach()chamada possa ser o valor padrão de this, mas por qualquer motivo, não; thisserá undefinedse esse segundo argumento não for fornecido.

(Nota: o material acima thisnão se aplica se o retorno de chamada for uma =>função, porque thisnunca estará vinculado a nada quando essas funções forem chamadas.)

Também é importante lembrar que existe toda uma família de utilitários semelhantes no protótipo Array, e muitas perguntas surgem no Stackoverflow sobre uma função ou outra, de modo que a melhor solução é simplesmente escolher uma ferramenta diferente. Você tem:

  • forEach por fazer algo com ou para cada entrada em uma matriz;
  • filter por produzir uma nova matriz contendo apenas entradas qualificadas;
  • map para criar uma nova matriz individual transformando uma matriz existente;
  • some verificar se pelo menos um elemento em uma matriz se encaixa em alguma descrição;
  • everyverificar se todas as entradas em uma matriz correspondem a uma descrição;
  • find procurar um valor em uma matriz

e assim por diante. Link MDN

Pontudo
fonte
34
Obrigado! ES6: arr.forEach ((o, i, a) => a [i] = myNewVal) #
DiegoRBaquero
por que part(ou mesmo oem es6) é indefinido? como obter valor iterado?
Daniil Mashkin
@DaniilMashkin partseria undefinedse um elemento da matriz fosse atribuído undefinedexplicitamente. Os slots "vazios" de uma matriz (uma entrada da matriz que nunca recebeu nenhum valor) são ignorados pela .forEach()maioria dos outros métodos de iteração da matriz.
Pointy
2
Para ser completo, .forEach()também é necessário um segundo argumento thisArg, que você pode usar como thisdentro do retorno de chamada. NOTA: este é um argumento de .forEachNÃO um argumento do retorno de chamada.
Moha, o Todo-Poderoso camelo
3
Para usar o thispassado como segundo argumento para .forEach(), você precisa passar a função de retorno de chamada usando a sintaxe function(), pois o uso da função de seta do ES6 () => {}não vincula o contexto.
jpenna
131

Vamos tentar simplificar e discutir como está realmente funcionando. Tem a ver com tipos de variáveis ​​e parâmetros de função.

Aqui está o seu código que estamos falando:

var arr = ["one","two","three"];

arr.forEach(function(part) {
  part = "four";
  return "four";
})

alert(arr);

Primeiro, aqui é onde você deve ler sobre Array.prototype.forEach ():

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach

Segundo, vamos falar brevemente sobre os tipos de valor em JavaScript.

Primitivas (indefinidas, nulas, String, Boolean, Number) armazenam um valor real.

ex: var x = 5;

Os tipos de referência (objetos personalizados) armazenam a localização da memória do objeto.

ex: var xObj = { x : 5 };

E terceiro, como os parâmetros de função funcionam.

Nas funções, os parâmetros são sempre passados ​​por valor.

Como arré uma matriz de Strings, é uma matriz de objetos primitivos , o que significa que eles são armazenados por valor.

Portanto, para o seu código acima, isso significa que toda vez que o forEach () itera, parté igual ao mesmo valor que arr[index], mas não o mesmo objeto .

part = "four";mudará a partvariável, mas deixará em arrpaz.

O código a seguir alterará os valores que você deseja:

var arr = ["one","two","three"];

arr.forEach(function(part, index) {
  arr[index] = "four";
});

alert(arr);

Agora, se a matriz arrfor uma matriz de tipos de referência , o código a seguir funcionará porque os tipos de referência armazenam um local de memória de um objeto em vez do objeto real.

var arr = [{ num : "one" }, { num : "two"}, { num : "three"}];

arr.forEach(function(part, index) {
  // part and arr[index] point to the same object
  // so changing the object that part points to changes the object that arr[index] points to

  part.num = "four";
});

alert(arr[0].num);
alert(arr[1].num);
alert(arr[2].num);

A seguir, ilustra que você pode alterar partpara apontar para um novo objeto enquanto deixa os objetos armazenados arrsozinhos:

var arr = [{ num : "one" }, { num : "two"}, { num : "three"}];

arr.forEach(function(part, index) {
  // the following will not change the object that arr[index] points to because part now points at a new object
  part = 5;
});

alert(arr[0].num);
alert(arr[1].num);
alert(arr[2].num);
Dave
fonte
2
de fato, ótima explicação! Teria sido ainda melhor estender a explicação nos outros métodos de iteração, para ... of, map, ... O novo for ... of funciona de uma maneira muito semelhante à forEach nesses casos, exceto que o " index "está ausente para alterar eventualmente a matriz original (como no forEach). Também o mapa ES5 parece semelhante ao forEach, talvez apenas a possibilidade de retornar valores de um mapa torne as coisas mais claras da perspectiva da sintaxe do que usar o índice.
Christophe Vidal
1
Ótima explicação. Obrigado. Afinal, não consigo entender esta afirmação: In functions, parameters are always passed by value. e o segundo exemplo?
20416 Alex
@ 7sides, Essa declaração está descrevendo que os parâmetros de função sempre serão passados ​​por valor. Portanto, para os primitivos, será o valor que o primitivo aponta. Para objetos, será o local que o objeto está apontando. esta página do w3schools tem uma boa explicação. Veja as seções Os argumentos são passados ​​por valor e os objetos são passados ​​por referência .
21416 Dave
Nas funções, os parâmetros são sempre passados ​​por valor. BAM. THX. 1
radarbob
92

Matriz: [1, 2, 3, 4]
Resultado:["foo1", "foo2", "foo3", "foo4"]

Array.prototype.map() Manter matriz original

const originalArr = ["Iron", "Super", "Ant", "Aqua"];
const modifiedArr = originalArr.map(name => `${name}man`);

console.log( "Original: %s", originalArr );
console.log( "Modified: %s", modifiedArr );

Array.prototype.forEach() Substituir matriz original

const originalArr = ["Iron", "Super", "Ant", "Aqua"];
originalArr.forEach((name, index) => originalArr[index] = `${name}man`);

console.log( "Overridden: %s", originalArr );

Roko C. Buljan
fonte
3
"Array.prototype.map () Modifica a matriz original, obtida com a reatribuição da variável arr" .map () nunca modifica a matriz original, ela cria uma nova. A reatribuição da variável não altera o objeto original.
vadkou 18/02/19
1
let arr1 = ["1", 2, 3, 4]; arr1.map(function(v) { return "foo"+ v; }); console.log( arr ); Array.prototype.map () Nunca modifique a matriz original, para cada mutação.
Anupam Maurya
@AnupamMaurya Isso não é verdade. mapdefinitivamente pode alterar sua matriz e forEach, em geral, não.
user4642212
@SebastianSimon, obrigado pela resposta. Tudo o que eu quero adicionar, pois Cada substitui os dados, mas um mapa, não pode, ele cria uma nova cópia.
Anupam Maurya
14

Javascript é transmitido por valor, e o que essencialmente significa parté uma cópia do valor na matriz.

Para alterar o valor, acesse a própria matriz no seu loop.

arr[index] = 'new value';

hvgotcodes
fonte
Depende do tipo de partse ele é copiado - embora você está certo de que a variável não é um ponteiro, mas um valor
Bergi
8
Dizer "JavaScript é transmitido por valor" é uma generalização grosseira. Existem tipos de referência em JavaScript. Tipos de valor são passados ​​por valor.
Alex Turpin
9
Não é uma generalização grosseira. Uma afirmação completamente correta e absoluta. Javascript passa referências por valor. Javascript é passado por valor, sempre.
Hvgotcodes
1
@ Bergi: Não, não importa o tipo. Todos os valores são copiados na atribuição - os únicos valores em JavaScript são primitivos e referências. Primitivas e referências são copiadas na atribuição.
precisa saber é o seguinte
6
@ Bergi só porque uma linguagem tem uma coisa chamada referência, não significa que ela usa passagem por referência. As referências podem ser (e são) passadas por valor, ou seja, o valor da referência é copiado e essa cópia é usada como argumento.
Hvgotcodes 19/09/12
4

substitua-o pelo índice da matriz.

array[index] = new_value;
Asif Alamgir
fonte
2
Legal ... Sempre adicionar alguma explicação
DevPro
4

Aqui está uma resposta semelhante usando uma =>função de estilo:

var data = [1,2,3,4];
data.forEach( (item, i, self) => self[i] = item + 10 );

dá o resultado:

[11,12,13,14]

O selfparâmetro não é estritamente necessário com a função de estilo de seta, portanto

data.forEach( (item,i) => data[i] = item + 10);

também funciona.

J. Peterson
fonte
3

A função .forEach pode ter uma função de retorno de chamada (eachelement, elementIndex). Então, basicamente, o que você precisa fazer é:

arr.forEach(function(element,index){
    arr[index] = "four";   //set the value  
});
console.log(arr); //the array has been overwritten.

Ou, se você quiser manter a matriz original, poderá fazer uma cópia antes de executar o processo acima. Para fazer uma cópia, você pode usar:

var copy = arr.slice();
zhujy_8833
fonte
3
Se você deseja fazer uma cópia da matriz, use em map()vez de forEach(). map()itera sobre a matriz de origem e retorna uma nova matriz contendo a cópia [modificada] do original: a matriz de origem permanece inalterada.
Nicholas Carey
2

Com os métodos de objeto Array, você pode modificar o conteúdo da matriz e, em comparação com o básico para loops, esses métodos não possuem uma funcionalidade importante. Você não pode modificar o índice em execução.

Por exemplo, se você remover o elemento atual e colocá-lo em outra posição de índice na mesma matriz, poderá fazê-lo facilmente. Se você mover o elemento atual para uma posição anterior, não haverá problema na próxima iteração, o próximo item será o mesmo, como se você não tivesse feito nada.

Considere esse código em que movemos o item na posição 5 do índice para a posição 2 quando o índice conta até 5.

var ar = [0,1,2,3,4,5,6,7,8,9];
ar.forEach((e,i,a) => {
i == 5 && a.splice(2,0,a.splice(i,1)[0])
console.log(i,e);
}); // 0 0 - 1 1 - 2 2 - 3 3 - 4 4 - 5 5 - 6 6 - 7 7 - 8 8 - 9 9

No entanto, se movermos o elemento atual para algum lugar além da posição atual do índice, as coisas ficam um pouco confusas. Em seguida, o item seguinte mudará para a posição de itens movidos e, na próxima iteração, não poderemos vê-lo ou avaliá-lo.

Considere este código em que movemos o item na posição 5 do índice para a posição 7, depois que o índice conta até 5.

var a = [0,1,2,3,4,5,6,7,8,9];
a.forEach((e,i,a) => {
i == 5 && a.splice(7,0,a.splice(i,1)[0])
console.log(i,e);
}); // 0 0 - 1 1 - 2 2 - 3 3 - 4 4 - 5 5 - 6 7 - 7 5 - 8 8 - 9 9

Portanto, nunca encontramos 6 no circuito. Normalmente, em um loop for, é esperado que você diminua o valor do índice ao mover o item da matriz para frente, para que o índice permaneça na mesma posição na próxima execução e você ainda possa avaliar o item deslocado para o lugar do item removido. Isso não é possível com métodos de matriz. Você não pode alterar o índice. Verifique o seguinte código

var a = [0,1,2,3,4,5,6,7,8,9];
a.forEach((e,i,a) => {
i == 5 && (a.splice(7,0,a.splice(i,1)[0]), i--);
console.log(i,e);
}); // 0 0 - 1 1 - 2 2 - 3 3 - 4 4 - 4 5 - 6 7 - 7 5 - 8 8 - 9 9

Como você vê quando diminuímos, iele não continuará de 5, mas 6, de onde foi deixado.

Portanto, mantenha isso em mente.

Redu
fonte
1

Para adicionar ou excluir completamente elementos que alterariam o índice, por meio da extensão da sugestão zhujy_8833 de slice () para iterar sobre uma cópia, basta contar o número de elementos que você já excluiu ou adicionou e alterou o índice de acordo. Por exemplo, para excluir elementos:

let values = ["A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8"];
let count = 0;
values.slice().forEach((value, index) => {
    if (value === "A2" || value === "A5") {
        values.splice(index - count++, 1);
    };
});
console.log(values);

// Expected: [ 'A0', 'A1', 'A3', 'A4', 'A6', 'A7', 'A8' ]

Para inserir elementos antes:

if (value === "A0" || value === "A6" || value === "A8") {
    values.splice(index - count--, 0, 'newVal');
};

// Expected: ['newVal', A0, 'A1', 'A2', 'A3', 'A4', 'A5', 'newVal', 'A6', 'A7', 'newVal', 'A8' ]

Para inserir elementos após:

if (value === "A0" || value === "A6" || value === "A8") {
    values.splice(index - --count, 0, 'newVal');
};

// Expected: ['A0', 'newVal', 'A1', 'A2', 'A3', 'A4', 'A5', 'A6', 'newVal', 'A7', 'A8', 'newVal']

Para substituir um elemento:

if (value === "A3" || value === "A4" || value === "A7") {
    values.splice(index, 1, 'newVal');
};

// Expected: [ 'A0', 'A1', 'A2', 'newVal', 'newVal', 'A5', 'A6', 'newVal', 'A8' ]

Nota: se implementar inserções 'antes' e 'depois', o código deve manipular 'antes' as inserções primeiro, o contrário não seria o esperado

Leigh Mathieson
fonte
0

Você pode tentar isso se quiser substituir

var newArray= [444,555,666];
var oldArray =[11,22,33];
oldArray.forEach((name, index) => oldArray [index] = newArray[index]);
console.log(newArray);
Shuhad zaman
fonte