web-dev-qa-db-ja.com

JavaScriptでforeachを実行するときに配列の値を変更することは可能ですか?

例:

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

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

alert(arr);

配列はまだ元の値のままですが、反復関数から配列の要素への書き込みアクセス権を持つ方法はありますか?

226
rsk82

コールバックには要素、インデックス、そして配列そのものが渡されます。

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

edit - コメントに記載されているように、.forEach()関数は2番目の引数を取ることができます。これは、コールバックを呼び出すたびにthisの値として使用されます。

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

2番目の例では、コールバックでarr自体がthisとして設定されています。 .forEach()呼び出しに関わる配列はdefaultthisの値かもしれませんが、何らかの理由でそうではありません。 2番目の引数が指定されていない場合、thisundefinedになります。

また、Arrayプロトタイプには類似したユーティリティのファミリーがすべて用意されていることを覚えておくことも重要です。また、Stackoverflowではさまざまな質問がポップアップ表示されます。あなたは持っています:

  • forEachは、配列内のすべてのエントリを使って、またはすべてのエントリに対して処理を行うためのものです。
  • 修飾エントリーのみを含む新しい配列を作成するためのfilter
  • 既存の配列を変換して1対1の新しい配列を作成するためのmap
  • 配列内の少なくとも1つの要素が何らかの記述に適合するかどうかをチェックするためのsome
  • 配列内のallエントリが説明と一致するかどうかをチェックするevery
  • 配列内の値を探すためのfind

等々。 MDNリンク

353
Pointy

それを単純にして、それが実際にどのように機能するのかを議論するためにを試してみましょう。それは変数型と関数パラメータと関係があります。

これが私たちが話しているあなたのコードです:

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

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

alert(arr);

まずはじめに、ここでArray.prototype.forEach()について読むべきところです。

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

次に、JavaScriptの値型について簡単に説明しましょう。

プリミティブ(未定義、null、String、Boolean、Number)は実際の値を格納します。

例:var x = 5;

参照型(カスタムオブジェクト)は、オブジェクトのメモリ位置を格納します。

例:var xObj = { x : 5 };

そして第三に、関数パラメータの働き。

関数では、パラメータは常に値で渡されます

arrは文字列の配列なので、プリミティブオブジェクトの配列です。つまり、値によって格納されます。

したがって、上記のコードでは、これは、forEach()が繰り返されるたびに、partarr[index]と同じ値になりますが、同じオブジェクトにはなりません。

part = "four";part変数を変更しますが、arrはそのままにします。

次のコードはあなたが望む値を変更します:

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

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

alert(arr);

配列arr参照型の配列である場合、参照型は実際のオブジェクトではなくオブジェクトのメモリ位置を格納するため、次のコードが機能します。

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

以下は、partに格納されているオブジェクトをそのままにして、arrを新しいオブジェクトを指すように変更できることを示しています。

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

配列:["1", 2, 3, 4]
結果:["foo1", "foo2", "foo3", "foo4"]

Array.prototype.map() 元の配列を保持する

新しい変数modifiedArrを導入することによって達成される

const arr = ["1", 2, 3, 4];

const modifiedArr = arr.map(v => `foo${v}`);

console.log( "Original:", arr );
console.log( "Modified:", modifiedArr );

Array.prototype.forEach() 元の配列を修正する

そのキーをインデックスarr[i]でターゲティングすることによって達成される

const arr = ["1", 2, 3, 4];

arr.forEach((v, i) => arr[i] = `foo${v}`);

console.log( "Original:", arr );
69
Roko C. Buljan

Javascriptは値渡しであり、これは基本的にpartが配列内の値のコピーコピーであることを意味します。

値を変更するには、ループ内で配列自体にアクセスします。

arr[index] = 'new value';

12
hvgotcodes

.forEach関数はコールバック関数(eachelement、elementIndex)を持つことができるので、基本的にあなたがする必要があるのは以下の通りです。

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

または、元の配列を保持したい場合は、上記の手順を実行する前にそのコピーを作成することができます。コピーするには、次のようにします。

var copy = arr.slice();
3
zhujy_8833

配列のインデックスに置き換えます。

array[index] = new_value;
2
Asif Alamgir

これは=>スタイルの関数を使った同様の答えです。

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

結果を出します。

[11,12,13,14]

selfパラメータは、矢印スタイル関数では必ずしも必要ではないので、

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

また働きます。

1
J. Peterson

Arrayオブジェクトメソッドでは、基本的なforループと比較してもArrayコンテンツを変更できますが、これらのメソッドには重要な機能が1つ欠けています。実行時にインデックスを変更することはできません。

たとえば、現在の要素を削除して同じ配列内の別のインデックス位置に配置する場合は、これを簡単に実行できます。現在の要素を前の位置に移動しても、次の繰り返しで問題は発生しません。何もしていない場合と同じ次の項目が表示されます。

このコードを考えてみましょう。ここで、インデックスが5までカウントしたら、インデックス位置5のアイテムをインデックス位置2に移動します。

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

しかし、現在の要素を現在のインデックス位置を超えた場所に移動すると、少し面倒になります。それから、次のアイテムは移動したアイテムの位置に移動し、次の反復ではそれを見たり評価したりすることはできなくなります。

このコードを考えてみましょう。ここでは、インデックスが5までカウントしたら、インデックス位置5のアイテムをインデックス位置7に移動します。

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

だから私たちはループで6に会ったことがない。通常、forループでは、次の実行でインデックスが同じ位置に留まるように配列アイテムを前方に移動すると、インデックス値が減少し、削除されたアイテムの場所に移動したアイテムを評価できます。これは配列メソッドでは不可能です。あなたはインデックスを変更することはできません。次のコードを確認してください

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

iをデクリメントするとわかるように、5から6ではなく、残ったところから続きます。

だからこれを覚えておいてください。

1
Redu

Sliceを拡張してコピーを繰り返すためにインデックスを変更する要素を完全に追加または削除するには、単に削除または追加した要素の数を数え、それに応じてインデックスを変更します。たとえば、要素を削除するには

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

前に要素を挿入するには:

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

後に要素を挿入するには:

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

要素を置き換えるには:

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

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

注: 'before'挿入と 'after'挿入の両方を実装している場合、コードは 'before'挿入を最初に処理する必要があります。それ以外の方法は予想どおりにはなりません。

0
Leigh Mathieson