¿Por qué parseInt produce NaN con Array # map?

291

Desde la red de desarrolladores de Mozilla :

[1,4,9].map(Math.sqrt)

rendirá:

[1,2,3]

¿Por qué entonces esto:

['1','2','3'].map(parseInt)

producir esto:

[1, NaN, NaN]

He probado en Firefox 3.0.1 y Chrome 0.3 y solo como descargo de responsabilidad, sé que esto no es una funcionalidad de navegador cruzado (no IE).

Descubrí que lo siguiente logrará el efecto deseado. Sin embargo, todavía no explica el comportamiento errante de parseInt.

['1','2','3'].map(function(i){return +i;}) // returns [1,2,3]
puntilla
fuente
15
Para perezosos: utilícelo .map(parseFloat)porque toma un solo parámetro.
63
O usar .map(Number).
Nikolai
2
puede arr.map (Math.floor) si desea números enteros sin una función enrollada a mano.
dandavis
@Nikolai user669677 ¡excelentes sugerencias! Votaría eso en una respuesta
BiAiB
¿Puede alguien explicar por qué parseInt analiza primer número correctamente y hace error para que no sea primer índice
bawa g

Respuestas:

478

La función de devolución de llamada Array.maptiene tres parámetros:

Desde la misma página de Mozilla que ha vinculado a:

la devolución de llamada se invoca con tres argumentos: el valor del elemento, el índice del elemento y el objeto Array que se atraviesa ".

Entonces, si llama a una función parseIntque realmente espera dos argumentos, el segundo argumento será el índice del elemento.

En este caso, terminó llamando parseIntcon radix 0, 1 y 2 a su vez. El primero es lo mismo que no proporcionar el parámetro, por lo que está predeterminado en función de la entrada (base 10, en este caso). La base 1 es una base numérica imposible, y 3 no es un número válido en la base 2:

parseInt('1', 0); // OK - gives 1
parseInt('2', 1); // FAIL - 1 isn't a legal radix
parseInt('3', 2); // FAIL - 3 isn't legal in base 2 

Entonces, en este caso, necesita la función de envoltura:

['1','2','3'].map(function(num) { return parseInt(num, 10); });

o con la sintaxis ES2015 +:

['1','2','3'].map(num => parseInt(num, 10));

(En ambos casos, es mejor suministrar explícitamente una raíz parseIntcomo se muestra, porque de lo contrario adivina la raíz en función de la entrada. En algunos navegadores más antiguos, un 0 inicial hizo que adivine octal, lo que tendió a ser problemático. adivina hexadecimal si la cadena comienza con 0x)

Alnitak
fuente
25

mapestá pasando un segundo argumento, que está (en muchos casos) desordenando el parseIntparámetro radix.

Si está usando guión bajo, puede hacer:

['10','1','100'].map(_.partial(parseInt, _, 10))

O sin guión bajo:

['10','1','100'].map(function(x) { return parseInt(x, 10); });

philfreo
fuente
18

Puede resolver este problema usando Number como función iteratee:

var a = ['0', '1', '2', '10', '15', '57'].map(Number);

console.log(a);

Sin el nuevo operador, Número puede usarse para realizar la conversión de tipo. Sin embargo, difiere de parseInt: no analiza la cadena y devuelve NaN si el número no se puede convertir. Por ejemplo:

console.log(parseInt("19asdf"));
console.log(Number("19asf"));

acontell
fuente
11

Voy a apostar que es algo funky pasando con el segundo parámetro de parseInt, la raíz. Por qué está rompiendo con el uso de Array.map y no cuando lo llamas directamente, no lo sé.

//  Works fine
parseInt( 4 );
parseInt( 9 );

//  Breaks!  Why?
[1,4,9].map( parseInt );

//  Fixes the problem
[1,4,9].map( function( num ){ return parseInt( num, 10 ) } );
Peter Bailey
fuente
1
parseInt solo asume octal si la cadena proporcionada comienza con un carácter 0.
Alnitak el
Oh ya ... eso es correcto. Intenta "adivinar" la raíz en función de la entrada. Lo siento por eso.
Peter Bailey, el
3

Puede usar la función de flecha ES2015 / ES6 y simplemente pasar el número a parseInt. El valor predeterminado para radix será 10

[10, 20, 30].map(x => parseInt(x))

O puede especificar explícitamente la raíz para una mejor legibilidad de su código.

[10, 20, 30].map(x => parseInt(x, 10))

En el ejemplo anterior, la raíz se establece explícitamente en 10

Vlad Bezden
fuente
1

Otra solución rápida (de trabajo):

var parseInt10 = function(x){return parseInt(x, 10);}

['0', '1', '2', '10', '15', '57'].map(parseInt10);
//[0, 1, 2, 10, 15, 57]
yonatanmn
fuente
0

parseIntEn mi humilde opinión, debe evitarse por esta misma razón. Puede envolverlo para hacerlo más seguro en estos contextos como este:

const safe = {
  parseInt: (s, opt) => {
    const { radix = 10 } = opt ? opt : {};
    return parseInt(s, radix);
  }
}

console.log( ['1','2','3'].map(safe.parseInt) );
console.log(
  ['1', '10', '11'].map(e => safe.parseInt(e, { radix: 2 }))
);

lodash / fp limita los argumentos iterativos a 1 de forma predeterminada para evitar estos problemas. Personalmente, he encontrado estas soluciones para crear tantos errores como eviten. La inclusión parseInten listas negras a favor de una implementación más segura es, creo, un mejor enfoque.

Doug Coburn
fuente