¿Hay alguna diferencia entre foreach y mapa?

218

Ok, esta es más una pregunta de informática, que una pregunta basada en un idioma en particular, pero ¿hay alguna diferencia entre una operación de mapa y una operación foreach? ¿O son simplemente nombres diferentes para la misma cosa?

Robert Gould
fuente
Curiosamente, si consigo una Iterator[String]de scala.io.Source.fromFile("/home/me/file").getLines()y aplicarlo .foreach(s => ptintln(s))a ella, se imprime bien, pero va directo vacía después. Al mismo tiempo, si lo solicito .map(ptintln(_)), simplemente se vacía y no se imprime nada.
Ivan

Respuestas:

294

Diferente.

foreach itera sobre una lista y aplica alguna operación con efectos secundarios a cada miembro de la lista (como guardar cada uno en la base de datos, por ejemplo)

map itera sobre una lista, transforma a cada miembro de esa lista y devuelve otra lista del mismo tamaño con los miembros transformados (como convertir una lista de cadenas en mayúsculas)

madlep
fuente
¡Gracias, pensé que era algo así, pero no estaba seguro!
Robert Gould
19
pero también podría tener efectos secundarios en la función de mapa. Me gusta esta respuesta: stackoverflow.com/a/4927981/375688
TinyTimZamboni
66
Un punto importante a mencionar (esto es particularmente cierto en Scala) es que una llamada a mapno no da lugar a la ejecución de su lógica subyacente, hasta cuando la lista de espera es convocado transformado. Por el contrario, foreachla operación se calcula de inmediato.
bigT
124

La diferencia importante entre ellos es que mapacumula todos los resultados en una colección, mientras que foreachno devuelve nada. mapgeneralmente se usa cuando desea transformar una colección de elementos con una función, mientras que foreachsimplemente ejecuta una acción para cada elemento.

Henk
fuente
¡Gracias! Ahora entiendo la diferencia. Había sido confuso para mí durante bastante tiempo
Robert Gould
También muy importante!
Мати Тернер
40

En resumen, foreaches para aplicar una operación en cada elemento de una colección de elementos, mientras que mapes para transformar una colección en otra.

Hay dos diferencias significativas entre foreachy map.

  1. foreachno tiene restricciones conceptuales sobre la operación que aplica, aparte de aceptar un elemento como argumento. Es decir, la operación puede no hacer nada, puede tener un efecto secundario, puede devolver un valor o puede no devolver un valor. Lo único que foreachimporta es iterar sobre una colección de elementos y aplicar la operación en cada elemento.

    map, por otro lado, tiene una restricción en la operación: espera que la operación devuelva un elemento, y probablemente también acepte un elemento como argumento. La mapoperación itera sobre una colección de elementos, aplicando la operación en cada elemento y finalmente almacenando el resultado de cada invocación de la operación en otra colección. En otras palabras, map transforma una colección en otra.

  2. foreachtrabaja con una sola colección de elementos. Esta es la colección de entrada.

    map funciona con dos colecciones de elementos: la colección de entrada y la colección de salida.

No es un error relacionar los dos algoritmos: de hecho, puede ver los dos jerárquicamente, donde maphay una especialización foreach. Es decir, puede usar foreachy hacer que la operación transforme su argumento y lo inserte en otra colección. Entonces, el foreachalgoritmo es una abstracción, una generalización, del mapalgoritmo. De hecho, debido a que foreachno tiene restricciones en su funcionamiento, podemos decir con seguridad que foreaches el mecanismo de bucle más simple que existe y que puede hacer cualquier cosa que un bucle pueda hacer. map, así como otros algoritmos más especializados, está ahí para la expresividad: si desea mapear (o transformar) una colección en otra, su intención es más clara si la usa mapque si la usa foreach.

Podemos extender más esta discusión y considerar el copyalgoritmo: un ciclo que clona una colección. Este algoritmo también es una especialización del foreachalgoritmo. Podría definir una operación que, dado un elemento, inserte ese mismo elemento en otra colección. Si utiliza foreachcon esa operación, en realidad realizó el copyalgoritmo, aunque con claridad, expresividad o explicidad reducidas. Vamos a ir aún más lejos: podemos decir que mapes una especialización de copy, en sí misma una especialización de foreach. mappuede cambiar cualquiera de los elementos sobre los que itera. Si mapno cambia ninguno de los elementos, simplemente copia los elementos y usa la copia expresaría la intención más claramente.

El foreachalgoritmo en sí puede o no tener un valor de retorno, dependiendo del idioma. En C ++, por ejemplo, foreachdevuelve la operación que recibió originalmente. La idea es que la operación podría tener un estado, y es posible que desee volver a inspeccionar cómo evolucionó sobre los elementos. map, también, puede o no devolver un valor. En C ++ transform(el equivalente mapaquí) sucede que devuelve un iterador al final del contenedor de salida (colección). En Ruby, el valor de retorno de mapes la secuencia de salida (colección). Entonces, el valor de retorno de los algoritmos es realmente un detalle de implementación; su efecto puede o no ser lo que devuelven.

Wilhelmtell
fuente
Para ver un ejemplo de cómo .forEach()se puede usar para implementar .map(), consulte aquí: stackoverflow.com/a/39159854/1524693
Chinoto Vokro
32

Array.protototype.mapmétodo y Array.protototype.forEachson bastante similares.

Ejecute el siguiente código: 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));
});

Dan el mismo resultado exacto.

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

Pero el giro se produce cuando ejecuta el siguiente código:

Aquí simplemente he asignado el resultado del valor de retorno del mapa y para cada método.

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

¡Ahora el resultado es algo complicado!

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

Conclusión

Array.prototype.mapdevuelve una matriz pero Array.prototype.forEachno lo hace. Por lo tanto, puede manipular la matriz devuelta dentro de la función de devolución de llamada pasada al método de mapa y luego devolverla.

Array.prototype.forEach solo recorre el conjunto dado para que pueda hacer sus cosas mientras recorre el conjunto.

abhisekp
fuente
11

La diferencia más "visible" es que el mapa acumula el resultado en una nueva colección, mientras que foreach se realiza solo para la ejecución misma.

pero hay un par de supuestos adicionales: dado que el "propósito" del mapa es la nueva lista de valores, en realidad no importa el orden de ejecución. de hecho, algunos entornos de ejecución generan código paralelo, o incluso introducen algunas memorias para evitar pedir valores repetidos, o pereza, para evitar llamar a algunos.

foreach, por otro lado, se llama específicamente por los efectos secundarios; por lo tanto, el orden es importante y generalmente no puede ser paralelizado.

Javier
fuente
11

Respuesta corta: map y forEachson diferentes. Además, hablando informalmente, mapes un estricto superconjunto de forEach.

Respuesta larga: Primero, elaboremos descripciones de una línea de forEachy map:

  • forEach itera sobre todos los elementos, llamando a la función proporcionada en cada uno.
  • map itera sobre todos los elementos, llama a la función proporcionada en cada uno y produce una matriz transformada al recordar el resultado de cada llamada a la función.

En muchos idiomas, a forEachmenudo se le llama justo each. La siguiente discusión usa JavaScript solo como referencia. Realmente podría ser cualquier otro idioma.

Ahora, usemos cada una de estas funciones.

Utilizando forEach:

Tarea 1: escriba una función printSquaresque acepte una matriz de números arre imprima el cuadrado de cada elemento en ella.

Solución 1:

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

Utilizando map:

Tarea 2: Escriba una función selfDot, que acepta una matriz de números arr, y devuelve una matriz en la que cada elemento es el cuadrado del elemento correspondiente en arr.

Aparte: Aquí, en términos de argot, estamos tratando de cuadrar la matriz de entrada. En términos formales, estamos tratando de calcular su producto punto consigo mismo.

Solución 2:

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

¿Cómo es mapun superconjunto de forEach?

Puede usar mappara resolver ambas tareas, Tarea 1 y Tarea 2 . Sin embargo, no puede usar forEachpara resolver la Tarea 2 .

En la Solución 1 , si simplemente reemplaza forEachpor map, la solución seguirá siendo válida. Sin embargo, en la Solución 2 , reemplazar mappor forEachromperá su solución que funcionaba anteriormente.

Implementando forEachen términos demap :

Otra forma de darse cuenta mapde la superioridad es implementar forEachen términos de map. Como somos buenos programadores, no nos permitiremos contaminar el espacio de nombres. Llamaremos a nuestro forEach, solo each.

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

Ahora, si no te gustan las prototypetonterías, aquí tienes:

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

Entonces, umm .. ¿Por qué forEach existe?

La respuesta es la eficiencia. Si no está interesado en transformar una matriz en otra matriz, ¿por qué debería calcular la matriz transformada? ¿Solo para tirarlo? ¡Por supuesto no! Si no quieres una transformación, no debes hacer una transformación.

Entonces, aunque el mapa se puede usar para resolver la Tarea 1 , probablemente no debería. Para cada uno es el candidato adecuado para eso.


Respuesta original:

Si bien estoy en gran medida de acuerdo con la respuesta de @madlep, me gustaría señalar que map()es un súper conjunto estricto de forEach().

Sí, map()generalmente se usa para crear una nueva matriz. Sin embargo, también se puede usar para cambiar la matriz actual.

Aquí hay un ejemplo:

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!!"]

En el ejemplo anterior, ase configuró convenientemente de modo que a[i] === ipara i < a.length. Aun así, demuestra el poder de map().

Aquí está la descripción oficial demap() . Tenga en cuenta que map()incluso puede cambiar la matriz en la que se llama. Granizo map().

Espero que esto haya ayudado.


Editado el 10 de noviembre de 2015: elaboración adicional.

Sumukh Barve
fuente
-1, ya que esto solo es válido bajo javascript; mientras que la pregunta se refiere específicamente a conceptos agnósticos de lenguaje y ciencias de la computación, no a implementaciones limitadas.
Javier
1
@Javier: Hmm .., tengo que estar de acuerdo contigo acerca de que mi respuesta sea específica de JavaScript. Pero pregúntese esto: si un idioma tenía una mapfunción nativa pero no forEach; ¿No podrías simplemente no usar en maplugar de forEach? Por otro lado, si un idioma tuviera forEachpero no map, tendrías que implementar el tuyo map. No podrías simplemente usar en forEachlugar de map. Dime que piensas.
Sumukh Barve
3

Aquí hay un ejemplo en Scala usando listas: el mapa devuelve la lista, foreach no devuelve nada.

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

Entonces map devuelve la lista resultante de aplicar la función f a cada elemento de la lista:

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 solo aplica f a cada elemento:

scala> var sum = 0
sum: Int = 0

scala> list foreach (sum += _)

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

Si está hablando de Javascript en particular, la diferencia es que mapes una función de bucle mientras que forEaches un iterador.

Úselo mapcuando desee aplicar una operación a cada miembro de la lista y recuperar los resultados como una nueva lista, sin afectar la lista original.

Úselo forEachcuando desee hacer algo en función de cada elemento de la lista. Es posible que esté agregando cosas a la página, por ejemplo. Esencialmente, es ideal para cuando quieres "efectos secundarios".

Otras diferencias: forEachno devuelve nada (ya que es realmente una función de flujo de control), y la función transferida obtiene referencias al índice y a la lista completa, mientras que map devuelve la nueva lista y solo pasa en el elemento actual.

trata bien tus modificaciones
fuente
0

ForEach intenta aplicar una función como escribir en db, etc. en cada elemento del RDD sin devolver nada.

Pero map()aplica alguna función sobre los elementos de rdd y devuelve el rdd. Entonces, cuando ejecuta el método a continuación, no fallará en la línea 3, pero al recopilar el rdd después de aplicar foreach, fallará y arrojará un error que dice

Archivo "<stdin>", línea 5, en <module>

AttributeError: el objeto 'NoneType' no tiene el atributo '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())
usuario2456047
fuente