Dlaczego parsowanie daje NaN z mapą Array #?

290

Z sieci deweloperów Mozilla :

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

da:

[1,2,3]

Dlaczego więc to robi:

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

uzyskaj to:

[1, NaN, NaN]

Testowałem w Firefoksie 3.0.1 i Chrome 0.3 i jako zrzeczenie się odpowiedzialności wiem, że nie jest to funkcja przeglądarki internetowej (bez IE).

Dowiedziałem się, że następujące efekty osiągną pożądany efekt. Jednak nadal nie wyjaśnia to błędnego zachowania parseInt.

['1','2','3'].map(function(i){return +i;}) // returns [1,2,3]
ćwiek
źródło
15
Dla leniwych: użyj, .map(parseFloat)ponieważ zajmuje tylko jeden parametr.
63
Lub użyj .map(Number).
Nikolai
2
możesz arr.map (Math.floor), jeśli chcesz Liczby całkowite bez funkcji ręcznego zwijania.
dandavis
@Nikolai user669677 świetne sugestie! Głosowałbym za tym w odpowiedzi
BiAiB
czy ktoś może wyjaśnić, dlaczego parsowanie poprawnie parsuje pierwszą liczbę i robi błąd dla innego niż pierwszy indeks
bawa g

Odpowiedzi:

478

Funkcja oddzwaniania Array.mapma trzy parametry:

Z tej samej strony Mozilli , do której linkujesz:

wywołanie zwrotne jest wywoływane z trzema argumentami: wartością elementu, indeksem elementu i przemierzanym obiektem Array. "

Więc jeśli wywołasz funkcję, parseIntktóra faktycznie oczekuje dwóch argumentów, drugim argumentem będzie indeks elementu.

W tym przypadku zakończyłeś dzwonienie parseIntz radix 0, 1 i 2 z kolei. Pierwszy jest taki sam, jak brak podania parametru, więc domyślnie oparty na danych wejściowych (w tym przypadku podstawa 10). Baza 1 jest niemożliwą bazą liczb, a 3 nie jest prawidłową liczbą w bazie 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 

W takim przypadku potrzebujesz funkcji otoki:

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

lub ze składnią ES2015 +:

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

(W obu przypadkach najlepiej jest jawnie podać podstawkę, parseIntjak pokazano, ponieważ inaczej odgaduje podstawę na podstawie danych wejściowych. W niektórych starszych przeglądarkach wiodące 0 powodowało, że zgadł liczbę ósemkową, co zwykle było problematyczne. zgadnij hex, jeśli ciąg zaczyna się od 0x.)

Alnitak
źródło
25

mapprzekazuje drugi argument, którym jest (w wielu przypadkach) parseIntzepsucie parametru radix.

Jeśli używasz podkreślenia, możesz:

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

Lub bez podkreślenia:

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

philfreo
źródło
18

Możesz rozwiązać ten problem, używając numeru jako funkcji iteracji:

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

console.log(a);

Bez nowego operatora można użyć liczby do przeprowadzenia konwersji typu. Jednak różni się od parseInt: nie analizuje ciągu i zwraca NaN, jeśli liczby nie można przekonwertować. Na przykład:

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

Acontell
źródło
11

Założę się, że dzieje się coś dziwnego z drugim parametrem parsInt, radix. Dlaczego łamie się przy użyciu Array.map, a nie kiedy wywołujesz go bezpośrednio, nie wiem.

//  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
źródło
1
parseInt przyjmuje wartość ósemkową tylko wtedy, gdy podany ciąg zaczyna się od znaku 0.
Alnitak,
O tak ... zgadza się. Próbuje „odgadnąć” podstawkę na podstawie danych wejściowych. Przepraszam za to.
Peter Bailey,
3

Możesz użyć funkcji strzałki ES2015 / ES6 i po prostu przekazać numer do parsowania. Domyślna wartość dla radix to 10

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

Lub możesz jawnie określić podstawę dla lepszej czytelności kodu.

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

W powyższym przykładzie podstawa jawnie ustawiona na 10

Vlad Bezden
źródło
1

kolejna (działająca) szybka poprawka:

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

['0', '1', '2', '10', '15', '57'].map(parseInt10);
//[0, 1, 2, 10, 15, 57]
yonatanmn
źródło
0

parseIntZ tego właśnie powodu należy unikać IMHO. Możesz go owinąć, aby był bardziej bezpieczny w następujących kontekstach:

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

Argumenty lodash / fp caps iterują argumenty domyślnie na 1, aby uniknąć tych błędów. Osobiście znalazłem te obejścia, aby stworzyć tyle błędów, ile unikają. Czarna lista parseIntna korzyść bezpieczniejszego wdrożenia jest, moim zdaniem, lepszym podejściem.

Doug Coburn
źródło