web-dev-qa-db-ja.com

foreachとmapに違いはありますか?

わかりました、これは特定の言語に基づいた質問というよりも、コンピューターサイエンスの質問ですが、マップ操作とforeach操作に違いはありますか?または、それらは同じものに対して単に異なる名前ですか?

213
Robert Gould

違う。

foreachはリストを反復処理し、副作用を伴う操作を各リストメンバーに適用します(たとえば、各メンバーをデータベースに保存するなど)

mapはリストを反復処理し、そのリストの各メンバーを変換し、変換されたメンバーと同じサイズの別のリストを返します(文字列のリストを大文字に変換するなど)

288
madlep

それらの重要な違いは、mapはすべての結果をコレクションに蓄積するのに対して、foreachは何も返さないことです。 mapは通常、関数を使用して要素のコレクションを変換するときに使用されますが、foreachは各要素に対してアクションを実行するだけです。

119
Henk

要するに、foreachは要素のコレクションの各要素に操作を適用するためのものであり、mapはコレクションを別のコレクションに変換するためのものです。

foreachmapには2つの重要な違いがあります。

  1. foreachは、おそらく要素を引数として受け入れることを除いて、適用する操作に概念的な制限はありません。つまり、操作は何もしない、副作用がある、値を返す、または値を返さない場合があります。 foreachが気にするのは、要素のコレクションを反復処理し、各要素に操作を適用することです。

    一方、mapには、操作に制限があります。操作が要素を返すことを期待し、おそらく要素を引数として受け入れることもあります。 map操作は要素のコレクションを反復処理し、各要素に操作を適用し、最後に操作の各呼び出しの結果を別のコレクションに保存します。言い換えると、maptransformsあるコレクションから別のコレクションへ。

  2. foreachは、単一の要素コレクションで機能します。これは入力コレクションです。

    mapは、入力コレクションと出力コレクションの2つの要素コレクションで機能します。

2つのアルゴリズムを関連付けることは間違いではありません。実際、2つを階層的に表示できます。ここで、mapforeachの特殊化です。つまり、foreachを使用して、操作で引数を変換し、別のコレクションに挿入できます。したがって、foreachアルゴリズムは、mapアルゴリズムの抽象化、一般化です。実際、foreachにはその動作に制限がないため、foreachが最も単純なループメカニズムであり、ループでできることは何でも実行できます。 map、および他のより特殊なアルゴリズムは、表現力を高めるためにあります。あるコレクションを別のコレクションにマッピング(または変換)する場合、mapを使用する場合よりもforeachを使用する場合の方が明確です。

この議論をさらに拡張して、copyアルゴリズム、つまりコレクションを複製するループを検討することができます。このアルゴリズムもforeachアルゴリズムの特殊化です。要素を指定すると、その同じ要素を別のコレクションに挿入する操作を定義できます。その操作でforeachを使用すると、明確さ、表現力、または明示性は低下しますが、実際にはcopyアルゴリズムを実行しました。さらに詳しく見てみましょう:mapcopyの特殊化であり、それ自体がforeachの特殊化であると言えます。 mapchange反復する要素のいずれかです。 mapがどの要素も変更しない場合は、単にコピー要素であり、copyを使用すると意図がより明確に表現されます。

foreachアルゴリズム自体は、言語に応じて戻り値を持つ場合と持たない場合があります。たとえば、C++では、foreachは最初に受け取った操作を返します。考えは、操作に状態がある場合があり、その操作を元に戻して、要素上でどのように進化したかを検査することです。 mapも、値を返す場合と返さない場合があります。 C++では、transform(ここではmapと同等)は、イテレータを出力コンテナ(コレクション)の最後に返すことがあります。 Rubyでは、mapの戻り値は出力シーケンス(コレクション)です。したがって、アルゴリズムの戻り値は実際には実装の詳細です。それらの効果は、それらが返すものである場合とそうでない場合があります。

40
wilhelmtell

Array.protototype.mapメソッドとArray.protototype.forEachはどちらも非常に似ています。

次のコードを実行します。 http://labs.codecademy.com/bw1/6#:workspace

var arr = [1, 2, 3, 4, 5];

arr.map(function(val, ind, arr){
    console.log("arr[" + ind + "]: " + Math.pow(val,2));
});

console.log();

arr.forEach(function(val, ind, arr){
    console.log("arr[" + ind + "]: " + Math.pow(val,2));
});

それらは正確な結果を与える

arr[0]: 1
arr[1]: 4
arr[2]: 9
arr[3]: 16
arr[4]: 25

arr[0]: 1
arr[1]: 4
arr[2]: 9
arr[3]: 16
arr[4]: 25

しかし、次のコードを実行するとねじれが生じます。

ここでは、マップおよびforEachメソッドからの戻り値の結果を単純に割り当てました。

var arr = [1, 2, 3, 4, 5];

var ar1 = arr.map(function(val, ind, arr){
    console.log("arr[" + ind + "]: " + Math.pow(val,2));
    return val;
});

console.log();
console.log(ar1);
console.log();

var ar2 = arr.forEach(function(val, ind, arr){
    console.log("arr[" + ind + "]: " + Math.pow(val,2));
    return val;
});

console.log();
console.log(ar2);
console.log();

結果はトリッキーになりました!

arr[0]: 1
arr[1]: 4
arr[2]: 9
arr[3]: 16
arr[4]: 25

[ 1, 2, 3, 4, 5 ]

arr[0]: 1
arr[1]: 4
arr[2]: 9
arr[3]: 16
arr[4]: 25

undefined

結論

Array.prototype.mapは配列を返しますが、Array.prototype.forEachは返しません。したがって、mapメソッドに渡されたコールバック関数内で返された配列を操作して、それを返すことができます。

Array.prototype.forEachは指定された配列のみをウォークスルーするため、アレイをウォークしている間に何かを行うことができます。

31
abhisekp

簡単な答え:mapforEachは異なります。また、非公式に言えば、mapforEachの厳密なスーパーセットです。

長答:最初に、forEachmapの1行の説明を考えてみましょう。

  • forEachは、すべての要素を反復処理し、それぞれに対して指定された関数を呼び出します。
  • mapは、すべての要素を反復処理し、それぞれに対して提供された関数を呼び出し、各関数呼び出しの結果を記憶することにより、変換された配列を生成します。

多くの言語では、forEachは単にeachと呼ばれます。以下の説明では、参照のみにJavaScriptを使用しています。本当に他の言語でもかまいません。

それでは、これらの各機能を使用してみましょう。

forEachを使用:

タスク1:関数printSquaresを作成します。これは、数値の配列arrを受け入れ、各要素の平方を出力しますそれ。

ソリューション1:

var printSquares = function (arr) {
    arr.forEach(function (n) {
        console.log(n * n);
    });
};

mapを使用:

タスク2:関数selfDotを記述します。これは、数値の配列arrを受け入れ、各要素がarrの対応する要素の二乗。

余談ですが、ここでは、スラングで、入力配列を二乗しようとしています。正式に言えば、それ自体との内積を計算しようとしています。

ソリューション2:

var selfDot = function (arr) {
    return arr.map(function (n) {
        return n * n;
    });
};

mapはどのようにforEachのスーパーセットですか?

mapを使用して、両方のタスクタスク1およびタスク2。ただし、forEachを使用してタスク2を解決することはできません。

ソリューション1で、単にforEachmapに置き換えるだけでも、ソリューションは有効です。ただし、ソリューション2では、mapforEachに置き換えると、以前に機能していたソリューションが破損します。

forEachに関してmapを実装する:

mapの優位性を実現する別の方法は、forEachの観点からmapを実装することです。私たちは優れたプログラマーなので、名前空間の汚染にふけることはありません。 forEach、単にeachと呼びます。

Array.prototype.each = function (func) {
    this.map(func);
};

prototypeのナンセンスが気に入らなければ、ここに行きます:

var each = function (arr, func) {
    arr.map(func); // Or map(arr, func);
};

それで、ええと..なぜforEachは存在するのでしょうか?

答えは効率です。配列を別の配列に変換することに興味がない場合、変換された配列を計算する必要があるのはなぜですか?ダンプするだけですか?もちろん違います!変換が必要ない場合は、変換を実行しないでください。

したがって、mapを使用してTask 1を解決できますが、おそらくそうすべきではありません。それぞれがそのための正しい候補です。


元の回答:

@madlepの答えにはほぼ同意しますが、map()forEach()strict super-setであることを指摘したいと思います。

はい、通常、map()を使用して新しい配列を作成します。ただし、alsoを使用して現在の配列を変更することもできます。

以下に例を示します。

var a = [0, 1, 2, 3, 4], b = null;
b = a.map(function (x) { a[x] = 'What!!'; return x*x; });
console.log(b); // logs [0, 1, 4, 9, 16] 
console.log(a); // logs ["What!!", "What!!", "What!!", "What!!", "What!!"]

上記の例では、aa[i] === ii < a.lengthになるように便利に設定されていました。それでも、map()の力を示しています。

これはmap()の公式の説明ですmap()は、呼び出される配列を変更することさえあることに注意してください!あられmap()

これが役に立てば幸いです。


編集2015年11月10日:詳細を追加。

11
Sumukh Barve

最も「目に見える」違いは、マップが結果を新しいコレクションに蓄積するのに対し、foreachは実行自体に対してのみ行われることです。

ただし、いくつかの追加の仮定があります。マップの「目的」は値の新しいリストであるため、実行の順序は実際には重要ではありません。実際、一部の実行環境では並列コードが生成されます。また、繰り返し値の呼び出しを回避するためのメモ化や、一部の呼び出しを回避するための遅延が導入されています。

一方、foreachは副作用のために特別に呼び出されます。したがって、順序は重要であり、通常は並列化できません。

11
Javier

リストを使用したScalaの例を次に示します。mapはリストを返し、foreachは何も返しません。

def map(f: Int ⇒ Int): List[Int]
def foreach(f: Int ⇒ Unit): Unit

したがって、mapは、関数fを各リスト要素に適用した結果のリストを返します。

scala> val list = List(1, 2, 3)
list: List[Int] = List(1, 2, 3)

scala> list map (x => x * 2)
res0: List[Int] = List(2, 4, 6)

Foreachは各要素にfを適用するだけです:

scala> var sum = 0
sum: Int = 0

scala> list foreach (sum += _)

scala> sum
res2: Int = 6 // res1 is empty
3
irudyak

特にJavascriptについて話している場合、違いはmapがループ関数であり、forEachが反復子であるということです。

リストの各メンバーに操作を適用し、元のリストに影響を与えずに結果を新しいリストとして取得する場合は、mapを使用します。

リストの各要素に基づいてdoを行う場合は、forEachを使用します。たとえば、ページに物事を追加する場合があります。基本的に、「副作用」が必要な場合に最適です。

その他の違い:forEachは(実際には制御フロー関数であるため)何も返さず、渡された関数はインデックスとリスト全体への参照を取得しますが、mapは新しいリストを返し、現在の要素のみを渡します。

2
phyzome

ForEachは、何も返さずにRDDの各要素にdbなどへの書き込みなどの関数を適用しようとします。

ただし、map()はrddの要素に何らかの関数を適用し、rddを返します。したがって、以下のメソッドを実行すると、line3で失敗しませんが、foreachを適用した後にrddを収集するときに失敗し、エラーがスローされます

ファイル「<stdin>」、5行目、<module>

AttributeError: 'NoneType'オブジェクトには属性 'collect'がありません

nums = sc.parallelize([1,2,3,4,5,6,7,8,9,10])
num2 = nums.map(lambda x: x+2)
print ("num2",num2.collect())
num3 = nums.foreach(lambda x : x*x)
print ("num3",num3.collect())
0
user2456047