è possibile cambiare i valori dell'array quando si fa foreach in javascript?


300

esempio:

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

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

alert(arr);

L'array è ancora con i suoi valori originali, c'è un modo per avere accesso in scrittura agli elementi dell'array dalla funzione iterante?



Prova la mappa ( developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… ):x=[2,3,4]; x=x.map(n=>n*2); // [4,6,8]
Justin

Risposte:


469

Al callback viene passato l'elemento, l'indice e l'array stesso.

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

modifica - come indicato in un commento, la .forEach()funzione può accettare un secondo argomento, che verrà utilizzato come valore di thisin ogni chiamata al callback:

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

Quel secondo esempio mostra che arrsi sta configurando come thisnel callback. Si potrebbe pensare che l'array coinvolto nella .forEach()chiamata possa essere il valore predefinito di this, ma per qualsiasi motivo non lo è; thissarà undefinedse quel secondo argomento non è fornito.

(Nota: le cose sopra sopra thisnon si applicano se il callback è una =>funzione, perché thisnon è mai associato a nulla quando vengono invocate tali funzioni.)

Inoltre, è importante ricordare che sul prototipo Array è disponibile un'intera famiglia di utilità simili e su Stackoverflow sorgono molte domande su una funzione o su un'altra in modo tale che la soluzione migliore sia semplicemente scegliere uno strumento diverso. Hai:

  • forEach per fare qualcosa con o per ogni voce in un array;
  • filter per la produzione di un nuovo array contenente solo voci idonee;
  • map per creare un nuovo array one-to-one trasformando un array esistente;
  • some per verificare se almeno un elemento in un array corrisponde a una descrizione;
  • everyper verificare se tutte le voci in un array corrispondono a una descrizione;
  • find per cercare un valore in un array

e così via. Collegamento MDN


34
Grazie! ES6: arr.forEach ((o, i, a) => a [i] = myNewVal)
DiegoRBaquero

perché part(o anche oin es6) non è definito? come ottenere valore iterato?
Daniil Mashkin il

@DaniilMashkin lo partsarebbe undefinedse un elemento dell'array fosse stato assegnato in modo undefinedesplicito. Gli slot "vuoti" di un array (una voce di array a cui non è mai stato assegnato alcun valore) vengono ignorati .forEach()e la maggior parte degli altri metodi di iterazione di array.
Punta il

2
Per completezza, .forEach()accetta anche un secondo argomento thisArg, che è possibile utilizzare come thisall'interno del callback. NOTA: questo è un argomento .forEachNON un argomento del callback.
Moha, l'onnipotente cammello, il

3
Per utilizzare il thispassato come secondo argomento .forEach(), è necessario passare la funzione di callback utilizzando la sintassi function(), poiché l'uso della funzione freccia di ES6 () => {}non vincola il contesto.
jpenna,

131

Diamo cercano di mantenerlo semplice e discutere su come si sta attualmente lavorando. Ha a che fare con tipi variabili e parametri di funzione.

Ecco il tuo codice di cui stiamo parlando:

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

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

alert(arr);

Prima di tutto, qui è dove dovresti leggere Array.prototype.forEach ():

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

In secondo luogo, parliamo brevemente dei tipi di valore in JavaScript.

Le primitive (undefined, null, String, Boolean, Number) memorizzano un valore effettivo.

ex: var x = 5;

I tipi di riferimento (oggetti personalizzati) memorizzano la posizione di memoria dell'oggetto.

ex: var xObj = { x : 5 };

E terzo, come funzionano i parametri di funzione.

Nelle funzioni, i parametri vengono sempre passati per valore.

Poiché arrè una matrice di stringhe, è una matrice di oggetti primitivi , il che significa che sono memorizzati per valore.

Quindi, per il tuo codice sopra, questo significa che ogni volta che forEach () itera, partè uguale allo stesso valore di arr[index], ma non allo stesso oggetto .

part = "four";cambierà la partvariabile, ma lascerà arrda solo.

Il seguente codice modificherà i valori desiderati:

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

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

alert(arr);

Ora se l'array arrera un array di tipi di riferimento , il seguente codice funzionerà perché i tipi di riferimento memorizzano una posizione di memoria di un oggetto anziché l'oggetto reale.

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);

Di seguito viene illustrato che è possibile modificare partper puntare a un nuovo oggetto lasciando gli oggetti archiviati arrda soli:

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);

2
anzi, ottima spiegazione! Sarebbe stato anche meglio estendere la spiegazione agli altri metodi di iterazione, per ... di, mappa, ... Il nuovo per ... di opere in un modo molto simile a quello di Ognuno per tali casi, tranne che il " indice "mancante per alterare eventualmente l'array originale (come in forEach). Anche la mappa ES5 sembra simile a Ognuno, forse solo la possibilità di restituire valori da una mappa rende le cose più chiare dal punto di vista della sintassi rispetto all'utilizzo dell'indice.
Christophe Vidal,

1
Ottima spiegazione Grazie. Dopo tutto non riesco a ottenere questa affermazione: In functions, parameters are always passed by value. che dire del secondo esempio?
Alex,

@ 7sides, Quell'affermazione sta descrivendo che i parametri di funzione saranno sempre passati per valore. Quindi, per le primitive, sarà il valore a cui punta la primitiva. Per gli oggetti, sarà la posizione a cui punta l'oggetto. questa pagina di w3schools ha una buona spiegazione. Vedere le sezioni Gli argomenti vengono passati per valore e gli oggetti vengono passati per riferimento .
Dave,

Nelle funzioni, i parametri vengono sempre passati per valore. BAM. grazie. +1
radarbob,

92

Matrice: [1, 2, 3, 4]
Risultato:["foo1", "foo2", "foo3", "foo4"]

Array.prototype.map() Mantieni matrice originale

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() Sostituisci array originale

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

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


3
"Array.prototype.map () Modifica l'array originale, ottenuto riassegnando la variabile arr" .map () non modifica mai l'array originale, ne crea uno nuovo. La riassegnazione della variabile non modifica l'oggetto originale.
Vadkou,

1
let arr1 = ["1", 2, 3, 4]; arr1.map(function(v) { return "foo"+ v; }); console.log( arr ); Array.prototype.map () Non modificare mai l'array originale, per ogni mutazione.
Anupam Maurya,

@AnupamMaurya Non è affatto vero. mappuò sicuramente mutare il suo array e forEach, in generale, no.
user4642212

@SebastianSimon, grazie per la risposta. Tutto quello che voglio aggiungere, poiché ogni sovrascrive i dati, ma una mappa non può, crea una nuova copia.
Anupam Maurya,

14

Javascript è pass by value e ciò significa essenzialmente che partè una copia del valore nell'array.

Per modificare il valore, accedi all'array stesso nel tuo loop.

arr[index] = 'new value';


Dipende dal tipo di partse viene copiato - anche se hai ragione sul fatto che la variabile non è un puntatore, ma un valore
Bergi

8
Dire "JavaScript è passato per valore" è una generalizzazione grossolana. Esistono tipi di riferimento in JavaScript. I tipi di valore vengono passati per valore.
Alex Turpin,

9
Non una generalizzazione grossolana. Un'affermazione completamente corretta e assoluta. Javascript passa riferimenti per valore. Javascript è pass per valore, sempre.
hvgotcodes,

1
@Bergi: No, non importa il tipo. Tutti i valori vengono copiati durante l'assegnazione - gli unici valori in JavaScript sono primitivi e riferimenti. Sia le primitive che i riferimenti vengono copiati durante il compito.
Newacct,

6
@Bergi solo perché una lingua ha una cosa chiamata riferimento, non significa che usi il passaggio per riferimento. I riferimenti possono essere (e sono) passati per valore, ovvero il valore del riferimento viene copiato e tale copia viene utilizzata come argomento.
hvgotcodes,

4

sostituirlo con l'indice dell'array.

array[index] = new_value;

2
Cool ... Aggiungi sempre qualche spiegazione
devpro

4

Ecco una risposta simile usando una =>funzione di stile:

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

dà il risultato:

[11,12,13,14]

Il selfparametro non è strettamente necessario con la funzione stile freccia, quindi

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

funziona anche.


3

La funzione .forEach può avere una funzione di callback (eachelement, elementIndex) Quindi, in sostanza, ciò che devi fare è:

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

Oppure, se si desidera mantenere l'array originale, è possibile crearne una copia prima di eseguire il processo sopra descritto. Per fare una copia, puoi usare:

var copy = arr.slice();

3
Se si desidera creare una copia dell'array, utilizzare map()invece di forEach(). map()scorre sull'array di origine e restituisce un nuovo array contenente la copia [modificata] dell'originale: l'array di origine rimane invariato.
Nicholas Carey,

2

Con i metodi degli oggetti Array è possibile modificare il contenuto dell'array ma confrontato con i loop di base, questi metodi mancano di un'importante funzionalità. Non è possibile modificare l'indice in fuga.

Ad esempio, se si rimuove l'elemento corrente e lo si posiziona in un'altra posizione di indice all'interno dello stesso array, è possibile farlo facilmente. Se sposti l'elemento corrente in una posizione precedente non ci sono problemi nella prossima iterazione otterrai lo stesso oggetto successivo come se non avessi fatto nulla.

Considera questo codice in cui spostiamo l'elemento nella posizione di indice 5 nella posizione di indice 2 una volta che l'indice conta fino a 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

Tuttavia, se spostiamo l'elemento corrente da qualche parte oltre l'attuale posizione dell'indice, le cose diventano un po 'confuse. Quindi l'elemento successivo si sposterà nella posizione degli oggetti spostati e nella successiva iterazione non saremo in grado di vederlo o valutarlo.

Considera questo codice in cui spostiamo l'elemento nella posizione di indice 5 nella posizione di indice 7 una volta che l'indice conta fino a 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

Quindi non abbiamo mai incontrato 6 nel ciclo. Normalmente in un ciclo for ci si aspetta che diminuisca il valore dell'indice quando si sposta l'elemento dell'array in avanti in modo che l'indice rimanga nella stessa posizione nella corsa successiva e sia ancora possibile valutare l'elemento spostato nella posizione dell'elemento rimosso. Questo non è possibile con i metodi array. Non è possibile modificare l'indice. Controlla il seguente codice

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

Come vedi quando diminuiamo i, non continuerà da 5 ma 6, da dove era rimasto.

Quindi tienilo a mente.


1

Per aggiungere o eliminare elementi interamente che altererebbero l'indice, mediante l'estensione del suggerimento zhujy_8833 di slice () per scorrere su una copia, è sufficiente contare il numero di elementi che sono già stati eliminati o aggiunti e modificare l'indice di conseguenza. Ad esempio, per eliminare elementi:

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' ]

Per inserire elementi prima:

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' ]

Per inserire elementi dopo:

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']

Per sostituire un 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 si implementano inserimenti 'prima' e 'dopo', il codice dovrebbe gestire prima gli inserimenti 'prima', altrimenti non sarebbe come previsto


0

Puoi provare questo se vuoi scavalcare

var newArray= [444,555,666];
var oldArray =[11,22,33];
oldArray.forEach((name, index) => oldArray [index] = newArray[index]);
console.log(newArray);
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.