Por que parseInt produz NaN com Array # map?

291

Na Rede de Desenvolvedores Mozilla :

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

produzirá:

[1,2,3]

Por que, então, faz isso:

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

produza isso:

[1, NaN, NaN]

Eu testei no Firefox 3.0.1 e no Chrome 0.3 e apenas como um aviso, sei que isso não é uma funcionalidade entre navegadores (sem IE).

Eu descobri que o seguinte realizará o efeito desejado. No entanto, ainda não explica o comportamento incorreto de parseInt.

['1','2','3'].map(function(i){return +i;}) // returns [1,2,3]
Brad
fonte
15
Para preguiçoso: use .map(parseFloat)porque leva apenas um parâmetro.
63
Ou use .map(Number).
Nikolai
2
você pode arr.map (Math.floor) se desejar Inteiros sem uma função manual.
dandavis
@Nikolai user669677 ótimas sugestões! Eu voto isso em uma
resposta
Alguém pode explicar por que parseInt analisa primeiro número corretamente e faz erro para que não seja primeiro índice
bawa g

Respostas:

478

A função de retorno de chamada em Array.maptem três parâmetros:

Na mesma página do Mozilla à qual você vinculou:

o retorno de chamada é chamado com três argumentos: o valor do elemento, o índice do elemento e o objeto Array que está sendo percorrido. "

Portanto, se você chamar uma função parseIntque realmente espera dois argumentos, o segundo argumento será o índice do elemento.

Nesse caso, você acabou chamando parseIntcom as raízes 0, 1 e 2. O primeiro é o mesmo que não fornecer o parâmetro, portanto, padronizou com base na entrada (base 10, neste caso). A base 1 é uma base numérica impossível e 3 não é um número válido na 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 

Portanto, neste caso, você precisa da função wrapper:

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

ou com a sintaxe do ES2015 +:

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

(Nos dois casos, é melhor fornecer explicitamente um radical parseIntcomo mostrado, porque, caso contrário, ele adivinha o radical com base na entrada. Em alguns navegadores mais antigos, um 0 inicial fazia com que adivinhasse octal, o que tendia a ser problemático. adivinhe hex se a string começar com 0x.)

Alnitak
fonte
25

mapestá passando um segundo argumento, que (em muitos casos) está atrapalhando o parseIntparâmetro radix.

Se você estiver usando sublinhado, poderá:

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

Ou sem sublinhado:

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

philfreo
fonte
18

Você pode resolver esse problema usando Number como função iteratee:

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

console.log(a);

Sem o novo operador, o Number pode ser usado para realizar a conversão de tipos. No entanto, difere de parseInt: não analisa a sequência e retorna NaN se o número não puder ser convertido. Por exemplo:

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

acontell
fonte
11

Aposto que é algo estranho acontecendo com o segundo parâmetro do parseInt, o radical. Por que está rompendo com o uso do Array.map e não quando você o chama diretamente, eu não sei.

//  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
fonte
1
parseInt assume apenas octal se a cadeia fornecida começar com um caractere 0.
Alnitak
Oh sim ... está certo. Ele tenta "adivinhar" o radical com base na entrada. Me desculpe por isso.
Peter Bailey
3

Você pode usar a função de seta ES2015 / ES6 e apenas passar o número para o parseInt. O valor padrão para a raiz será 10

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

Ou você pode especificar explicitamente radix para melhor legibilidade do seu código.

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

No exemplo acima, o radical definido explicitamente para 10

Vlad Bezden
fonte
1

outra solução rápida (funcionando):

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

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

parseIntO IMHO deve ser evitado por esse motivo. Você pode envolvê-lo para torná-lo mais seguro nesses 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 }))
);

O lodash / fp limita os argumentos de iteração para 1 por padrão para evitar essas dicas. Pessoalmente, eu encontrei essas soluções alternativas para criar tantos erros quanto eles evitarem. A lista negra a parseIntfavor de uma implementação mais segura é, penso eu, uma abordagem melhor.

Doug Coburn
fonte