Pourquoi parseInt produit-il NaN avec Array # map?


291

Du Mozilla Developer Network :

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

donnera:

[1,2,3]

Pourquoi alors cela:

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

donner ceci:

[1, NaN, NaN]

J'ai testé dans Firefox 3.0.1 et Chrome 0.3 et comme avertissement, je sais que ce n'est pas une fonctionnalité multi-navigateur (pas d'IE).

J'ai découvert que ce qui suit produira l'effet souhaité. Cependant, cela n'explique toujours pas le comportement errant de parseInt.

['1','2','3'].map(function(i){return +i;}) // returns [1,2,3]

15
Pour les paresseux: utilisez-le .map(parseFloat)car il ne prend qu'un seul paramètre.

63
Ou utilisez .map(Number).
Nikolai

2
vous pouvez arr.map (Math.floor) si vous voulez des entiers sans fonction roulée à la main.
dandavis

@Nikolai user669677 excellentes suggestions! Je voterais pour cela dans une réponse
BiAiB

quelqu'un peut-il expliquer pourquoi parseInt analyse correctement le premier nombre et fait une erreur pour autre que le premier index
bawa g

Réponses:


478

La fonction de rappel dans Array.mapa trois paramètres:

À partir de la même page Mozilla que vous avez liée à:

le rappel est invoqué avec trois arguments: la valeur de l'élément, l'index de l'élément et l'objet Array traversé. "

Donc, si vous appelez une fonction parseIntqui attend réellement deux arguments, le deuxième argument sera l'index de l'élément.

Dans ce cas, vous avez fini par appeler parseIntavec les radix 0, 1 et 2 tour à tour. Le premier est le même que ne fournissant pas le paramètre, il a donc par défaut basé sur l'entrée (base 10, dans ce cas). La base 1 est une base numérique impossible et 3 n'est pas un nombre valide dans 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 

Donc, dans ce cas, vous avez besoin de la fonction wrapper:

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

ou avec la syntaxe ES2015 +:

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

(Dans les deux cas, il est préférable de fournir explicitement un radix parseIntcomme indiqué, sinon il devine le radix en fonction de l'entrée. Dans certains navigateurs plus anciens, un 0 de tête le faisait deviner octal, ce qui tendait à être problématique. Il sera toujours devinez hex si la chaîne commence par 0x.)


25

maptransmet un deuxième argument, qui est (dans la plupart des cas) un fouillis pour parseIntle paramètre radix.

Si vous utilisez un trait de soulignement, vous pouvez faire:

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

Ou sans souligné:

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


18

Vous pouvez résoudre ce problème en utilisant Number comme fonction itérative:

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

console.log(a);

Sans le nouvel opérateur, Number peut être utilisé pour effectuer une conversion de type. Cependant, il diffère de parseInt: il n'analyse pas la chaîne et renvoie NaN si le nombre ne peut pas être converti. Par exemple:

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


11

Je parie que c'est quelque chose de génial qui se passe avec le deuxième paramètre de parseInt, le radix. Pourquoi cela rompt avec l'utilisation de Array.map et non lorsque vous l'appelez directement, je ne sais pas.

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

1
parseInt suppose uniquement octal si la chaîne fournie commence par un caractère 0.
Alnitak

Oh ouais ... c'est vrai. Il essaie de «deviner» le radix en fonction de l'entrée. Désolé pour ça.
Peter Bailey

3

Vous pouvez utiliser la fonction de flèche ES2015 / ES6 et simplement passer le numéro au parseInt. La valeur par défaut pour radix sera 10

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

Ou vous pouvez spécifier explicitement radix pour une meilleure lisibilité de votre code.

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

Dans l'exemple ci-dessus, radix est explicitement défini sur 10


1

une autre solution rapide (qui fonctionne):

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

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

0

parseIntIMHO doit être évitée pour cette raison même. Vous pouvez l'envelopper pour le rendre plus sûr dans ces contextes comme celui-ci:

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 limite les arguments itératifs à 1 par défaut pour éviter ces pièges. Personnellement, j'ai trouvé ces solutions de contournement pour créer autant de bugs qu'ils évitent. La mise sur liste noire parseInten faveur d'une mise en œuvre plus sûre est, je pense, une meilleure approche.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.