Warum liefert parseInt NaN mit Array # map?

291

Aus dem Mozilla Developer Network :

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

wird ergeben:

[1,2,3]

Warum macht das dann:

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

ergeben dies:

[1, NaN, NaN]

Ich habe in Firefox 3.0.1 und Chrome 0.3 getestet und nur als Haftungsausschluss weiß ich, dass dies keine browserübergreifende Funktionalität ist (kein IE).

Ich fand heraus, dass das Folgende den gewünschten Effekt erzielen wird. Es erklärt jedoch immer noch nicht das fehlerhafte Verhalten von parseInt.

['1','2','3'].map(function(i){return +i;}) // returns [1,2,3]
Brad
quelle
15
Für .map(parseFloat)Lazys : Verwenden Sie diese Option, da nur ein Parameter erforderlich ist.
63
Oder verwenden .map(Number).
Nikolai
2
Sie können arr.map (Math.floor) verwenden, wenn Sie Ganzzahlen ohne handgerollte Funktion möchten.
Dandavis
@ Nikolai user669677 tolle Vorschläge! Ich würde das in einer Antwort bestätigen
BiAiB
kann jemand erklären, warum parseInt die erste Zahl richtig analysiert und Fehler für andere als den ersten Index macht
bawa g

Antworten:

478

Die Rückruffunktion in Array.maphat drei Parameter:

Von derselben Mozilla-Seite , auf die Sie verlinkt haben:

Der Rückruf wird mit drei Argumenten aufgerufen: dem Wert des Elements, dem Index des Elements und dem Array-Objekt, das durchlaufen wird. "

Wenn Sie also eine Funktion aufrufen, parseIntdie tatsächlich zwei Argumente erwartet , ist das zweite Argument der Index des Elements.

In diesem Fall haben Sie parseIntnacheinander mit Radix 0, 1 und 2 angerufen . Der erste ist derselbe, als würde der Parameter nicht angegeben, daher wird er standardmäßig basierend auf der Eingabe (in diesem Fall Basis 10) verwendet. Basis 1 ist eine unmögliche Zahlenbasis und 3 ist keine gültige Zahl in Basis 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 

In diesem Fall benötigen Sie also die Wrapper-Funktion:

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

oder mit ES2015 + Syntax:

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

(In beiden Fällen ist es am besten, einen Radix wie gezeigt explizit anzugeben parseInt, da er sonst den Radix basierend auf der Eingabe errät. In einigen älteren Browsern führte eine führende 0 dazu, dass er ein Oktal erraten hat, was tendenziell problematisch war rate hex, wenn die Zeichenfolge mit beginnt 0x.)

Alnitak
quelle
25

mapgibt ein zweites Argument weiter, das (in vielen Fällen) den parseIntRadix-Parameter durcheinander bringt.

Wenn Sie einen Unterstrich verwenden, können Sie Folgendes tun:

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

Oder ohne Unterstrich:

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

Philfreo
quelle
18

Sie können dieses Problem mit Number als iteratee-Funktion lösen :

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

console.log(a);

Ohne den neuen Operator kann Number zur Typkonvertierung verwendet werden. Es unterscheidet sich jedoch von parseInt: Es analysiert den String nicht und gibt NaN zurück, wenn die Zahl nicht konvertiert werden kann. Zum Beispiel:

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

acontell
quelle
11

Ich werde wetten, dass mit dem zweiten Parameter von parseInt, dem Radix, etwas Funky los ist. Warum es mit der Verwendung von Array.map bricht und nicht, wenn Sie es direkt aufrufen, weiß ich nicht.

//  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
quelle
1
parseInt nimmt nur oktal an, wenn die angegebene Zeichenfolge mit einem 0-Zeichen beginnt.
Alnitak
Oh ja ... das stimmt. Es wird versucht, den Radix basierend auf der Eingabe zu "erraten". Das tut mir leid.
Peter Bailey
3

Sie können die Pfeilfunktion ES2015 / ES6 verwenden und die Nummer einfach an parseInt übergeben. Der Standardwert für radix ist 10

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

Oder Sie können Radix explizit angeben, um die Lesbarkeit Ihres Codes zu verbessern.

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

Im obigen Beispiel wird radix explizit auf 10 gesetzt

Vlad Bezden
quelle
1

eine andere (funktionierende) schnelle Lösung:

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

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

parseIntIMHO sollte aus genau diesem Grund vermieden werden. Sie können es einpacken, um es in folgenden Kontexten sicherer zu machen:

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 begrenzt die Argumente standardmäßig auf 1, um diese Fallstricke zu vermeiden. Persönlich habe ich festgestellt, dass diese Problemumgehungen so viele Fehler verursachen, wie sie vermeiden. Die schwarze Liste parseIntzugunsten einer sichereren Implementierung ist meines Erachtens ein besserer Ansatz.

Doug Coburn
quelle