apakah mungkin untuk mengubah nilai array ketika melakukan foreach dalam javascript?

300

contoh:

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

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

alert(arr);

Array masih dengan nilai aslinya, apakah ada cara untuk memiliki akses penulisan ke elemen array dari fungsi iterasi?

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

Jawaban:

469

Callback melewati elemen, indeks, dan array itu sendiri.

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

sunting - sebagaimana tercantum dalam komentar, .forEach()fungsi tersebut dapat mengambil argumen kedua, yang akan digunakan sebagai nilai thissetiap panggilan ke panggilan balik:

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

Bahwa contoh kedua menunjukkan arrdirinya sedang ditetapkan sebagai thisdi callback.One mungkin berpikir bahwa array yang terlibat dalam .forEach()panggilan mungkin standar nilai this, tapi apa pun alasannya itu tidak; thisakan terjadi undefinedjika argumen kedua tidak disediakan.

(Catatan: hal-hal di atas tentang thistidak berlaku jika panggilan balik adalah =>fungsi, karena thistidak pernah terikat pada apa pun ketika fungsi tersebut dipanggil.)

Juga penting untuk diingat bahwa ada seluruh keluarga utilitas serupa yang disediakan pada prototipe Array, dan banyak pertanyaan muncul di Stackoverflow tentang satu fungsi atau lainnya sehingga solusi terbaik adalah dengan hanya memilih alat yang berbeda. Kamu sudah memperoleh:

  • forEach untuk melakukan sesuatu dengan atau untuk setiap entri dalam array;
  • filter untuk menghasilkan array baru yang hanya berisi entri yang memenuhi syarat;
  • map untuk membuat array baru satu-ke-satu dengan mengubah array yang ada;
  • some untuk memeriksa apakah setidaknya satu elemen dalam array cocok dengan deskripsi;
  • everyuntuk memeriksa apakah semua entri dalam array cocok dengan deskripsi;
  • find untuk mencari nilai dalam array

dan seterusnya. Tautan MDN

Runcing
sumber
34
Terima kasih! ES6: arr.forEach ((o, i, a) => a [i] = myNewVal)
DiegoRBaquero
mengapa part(atau bahkan odalam es6) tidak terdefinisi? bagaimana cara mendapatkan nilai yang diulang?
Daniil Mashkin
@DaniilMashkin partakan undefinedjika elemen array telah ditetapkan undefinedsecara eksplisit. Slot "Kosong" dari sebuah array (entri array yang belum pernah diberi nilai apa pun) dilewati oleh .forEach()dan sebagian besar metode iterasi array lainnya.
Runcing
2
Untuk kelengkapan, .forEach()dibutuhkan juga argumen kedua thisArg, yang bisa Anda gunakan seperti thisdi dalam callback. CATATAN: argumen ini .forEachBUKAN argumen dari panggilan balik.
Moha the maha besar unta
3
Untuk menggunakan thisargumen yang diteruskan sebagai argumen kedua .forEach(), Anda harus meneruskan fungsi panggilan balik menggunakan sintaks function(), karena menggunakan fungsi panah ES6 () => {}tidak mengikat konteks.
jpenna
131

Mari kita coba untuk membuatnya tetap sederhana dan membahas bagaimana cara kerjanya. Ini ada hubungannya dengan tipe variabel dan parameter fungsi.

Inilah kode yang sedang kita bicarakan:

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

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

alert(arr);

Pertama, di sinilah Anda harus membaca tentang Array.prototype.forEach ():

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

Kedua, mari kita bicara secara singkat tentang tipe nilai dalam JavaScript.

Primitif (undefined, null, String, Boolean, Number) menyimpan nilai aktual.

ex: var x = 5;

Jenis Referensi (objek khusus) menyimpan lokasi memori objek.

ex: var xObj = { x : 5 };

Dan ketiga, bagaimana parameter fungsi bekerja.

Dalam fungsi, parameter selalu dilewati oleh nilai.

Karena arrarray dari String, ini adalah array objek primitif , yang berarti mereka disimpan oleh nilai.

Jadi untuk kode Anda di atas, ini berarti bahwa setiap kali forEach () iterates, partsama dengan nilai yang sama dengan arr[index], tetapi bukan objek yang sama .

part = "four";akan mengubah partvariabel, tetapi akan pergi arrsendiri.

Kode berikut akan mengubah nilai yang Anda inginkan:

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

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

alert(arr);

Sekarang jika array arradalah array dari tipe referensi , kode berikut akan berfungsi karena tipe referensi menyimpan lokasi memori dari suatu objek, bukan objek yang sebenarnya.

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

Berikut ini mengilustrasikan bahwa Anda dapat mengubah partuntuk menunjuk ke objek baru sambil membiarkan objek disimpan arrsendiri:

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
sumber
2
memang, penjelasan yang bagus! Akan lebih baik untuk memperluas penjelasan di metode iterasi lain, untuk ... dari, memetakan, ... yang baru untuk ... karya dalam cara yang sangat mirip daripada untuk setiap kasus seperti itu, kecuali bahwa " index "tidak ada untuk mengubah akhirnya array asli (seperti di forEach). Juga peta ES5 tampaknya mirip dengan forEach, mungkin saja kemungkinan untuk mengembalikan nilai dari peta membuat hal-hal lebih jelas dari perspektif sintaksis daripada menggunakan indeks.
Christophe Vidal
1
Penjelasan yang bagus. Terima kasih. Lagi pula saya tidak bisa mendapatkan pernyataan ini: In functions, parameters are always passed by value. Bagaimana dengan contoh kedua?
Alex
@ 7sides, Pernyataan itu menggambarkan bahwa parameter fungsi akan selalu diteruskan oleh nilai. Jadi untuk primitif, itu akan menjadi nilai yang ditunjukkan oleh primitif. Untuk objek, itu akan menjadi lokasi yang ditunjuk oleh objek. Halaman w3schools ini memiliki penjelasan yang bagus. Lihat bagian Argumen yang Diberi Nilai dan Objek Diberi oleh Referensi .
Dave
Dalam fungsi, parameter selalu dilewati oleh nilai. BAM. Terima kasih. +1
radarbob
92

Array: [1, 2, 3, 4]
Hasil:["foo1", "foo2", "foo3", "foo4"]

Array.prototype.map() Simpan array asli

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() Timpa array asli

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

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

Roko C. Buljan
sumber
3
"Array.prototype.map () Memodifikasi array asli, dicapai dengan menetapkan ulang variabel arr" .map () tidak pernah memodifikasi array asli, itu menciptakan yang baru. Penugasan kembali variabel tidak mengubah objek asli.
vadkou
1
let arr1 = ["1", 2, 3, 4]; arr1.map(function(v) { return "foo"+ v; }); console.log( arr ); Array.prototype.map () Jangan pernah Modifikasi array asli, forEach bermutasi.
Anupam Maurya
@AnupamMaurya Itu tidak benar sama sekali. mappasti dapat mengubah arraynya dan forEach, secara umum, tidak.
user4642212
@SebastianSimon, terima kasih atas jawabannya. Yang ingin saya tambahkan, forEach menimpa data, tetapi peta, tidak bisa, itu membuat salinan baru.
Anupam Maurya
14

Javascript diberikan nilai, dan yang pada dasarnya berarti partadalah salinan nilai dalam array.

Untuk mengubah nilainya, akses array itu sendiri di loop Anda.

arr[index] = 'new value';

hvgotcodes
sumber
Itu tergantung pada jenis partapakah itu disalin - walaupun Anda benar bahwa variabel bukan pointer, tetapi nilai
Bergi
8
Mengatakan "JavaScript lewat nilai" adalah generalisasi bruto. Ada jenis referensi dalam JavaScript. Jenis nilai dilewatkan oleh nilai.
Alex Turpin
9
Bukan generalisasi kotor. Pernyataan yang sepenuhnya benar dan mutlak. Javascript melewati referensi berdasarkan nilai. Javascript selalu lulus dengan nilai.
hvgotcodes
1
@Bergi: Tidak, tidak masalah jenisnya. Semua nilai disalin saat penugasan - satu-satunya nilai dalam JavaScript adalah primitif dan referensi. Primitif dan referensi disalin pada saat bertugas.
Newacct
6
@Bergi hanya karena bahasa memiliki sesuatu yang disebut referensi, tidak berarti menggunakan pass by reference. Referensi dapat (dan) diteruskan oleh nilai, yaitu nilai referensi disalin dan salinan itu digunakan sebagai argumen.
hvgotcodes
4

ganti dengan indeks array.

array[index] = new_value;
Asif Alamgir
sumber
2
Keren ... Selalu tambahkan beberapa penjelasan
devpro
4

Inilah jawaban yang mirip menggunakan =>fungsi gaya:

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

memberikan hasilnya:

[11,12,13,14]

The selfparameter tidak benar-benar diperlukan dengan fungsi panah gaya, sehingga

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

juga berfungsi.

J. Peterson
sumber
3

Fungsi .forEach dapat memiliki fungsi callback (setiap elemen, elementIndex) Jadi pada dasarnya apa yang perlu Anda lakukan adalah:

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

Atau jika Anda ingin menyimpan array asli, Anda dapat membuat salinannya sebelum melakukan proses di atas. Untuk membuat salinan, Anda dapat menggunakan:

var copy = arr.slice();
zhujy_8833
sumber
3
Jika Anda ingin membuat salinan array, gunakan map()bukan forEach(). map()beralih ke larik sumber dan mengembalikan larik baru yang berisi salinan asli [yang dimodifikasi]: larik sumber tidak berubah.
Nicholas Carey
2

Dengan metode objek Array Anda dapat memodifikasi konten Array namun dibandingkan dengan dasar untuk loop, metode ini tidak memiliki satu fungsi penting. Anda tidak dapat mengubah indeks saat dijalankan.

Misalnya jika Anda akan menghapus elemen saat ini dan menempatkannya ke posisi indeks lain dalam array yang sama Anda dapat dengan mudah melakukan ini. Jika Anda memindahkan elemen saat ini ke posisi sebelumnya tidak ada masalah di iterasi berikutnya Anda akan mendapatkan item berikutnya yang sama seolah-olah Anda tidak melakukan apa-apa.

Pertimbangkan kode ini tempat kami memindahkan item pada posisi indeks 5 ke posisi indeks 2 setelah indeks menghitung hingga 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

Namun jika kita memindahkan elemen saat ini ke suatu tempat di luar posisi indeks saat ini, segalanya menjadi sedikit berantakan. Maka item berikutnya akan bergeser ke posisi item yang dipindahkan dan dalam iterasi berikutnya kita tidak akan dapat melihat atau mengevaluasinya.

Pertimbangkan kode ini di mana kami memindahkan item di posisi indeks 5 ke posisi indeks 7 setelah indeks menghitung hingga 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

Jadi kita belum pernah bertemu 6 di loop. Biasanya dalam for loop Anda diharapkan mengurangi nilai indeks ketika Anda memindahkan item array ke depan sehingga indeks Anda tetap pada posisi yang sama di jalankan berikutnya dan Anda masih dapat mengevaluasi item yang bergeser ke tempat item yang dihapus. Ini tidak mungkin dengan metode array. Anda tidak dapat mengubah indeks. Periksa kode berikut

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

Seperti yang Anda lihat ketika kita mengurangi i itu tidak akan melanjutkan dari 5 tetapi 6, dari tempat itu ditinggalkan.

Jadi ingatlah ini.

Redu
sumber
1

Untuk menambah atau menghapus elemen seluruhnya yang akan mengubah indeks, dengan cara memperpanjang zhujy_8833 saran slice () untuk beralih pada salinan, cukup hitung jumlah elemen yang telah Anda hapus atau tambahkan dan ubah indeks sesuai. Misalnya, untuk menghapus elemen:

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

Untuk memasukkan elemen sebelumnya:

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

Untuk memasukkan elemen setelah:

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

Untuk mengganti elemen:

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

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

Catatan: jika menerapkan sisipan 'sebelum' dan 'setelah', kode harus menangani sisipan 'sebelum' terlebih dahulu, sebaliknya tidak akan seperti yang diharapkan

Leigh Mathieson
sumber
0

Anda dapat mencoba ini jika ingin menimpa

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