Почему parseInt дает NaN с картой Array #?

291

Из Сети разработчиков Mozilla :

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

даст:

[1,2,3]

Почему тогда это так:

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

дать это:

[1, NaN, NaN]

Я тестировал в Firefox 3.0.1 и Chrome 0.3 и, как заявление об отказе от ответственности, я знаю, что это не кросс-браузерная функциональность (нет IE).

Я узнал, что следующее достигнет желаемого эффекта. Однако, это все еще не объясняет странное поведение parseInt.

['1','2','3'].map(function(i){return +i;}) // returns [1,2,3]
штифтик
источник
15
Для lazy's: используйте, .map(parseFloat)потому что он принимает только один параметр.
63
Или используйте .map(Number).
Николай
2
Вы можете использовать arr.map (Math.floor), если хотите получить целые числа без функции, созданной вручную.
dandavis
@Nikolai user669677 отличные предложения! Я бы подтвердил это в ответе
BiAiB
Может кто-нибудь объяснить, почему parseInt правильно анализирует первый номер и делает ошибку для другого индекса
bawa g

Ответы:

478

Функция обратного вызова в Array.mapимеет три параметра:

С той же страницы Mozilla, на которую вы ссылались:

обратный вызов вызывается с тремя аргументами: значением элемента, индексом элемента и объектом Array, по которому выполняется обход. "

Так что если вы вызываете функцию, parseIntкоторая на самом деле ожидает два аргумента, вторым аргументом будет индекс элемента.

В этом случае вы в конечном итоге коллировали parseIntс основанием 0, 1 и 2 по очереди. Первый - это то же самое, что не предоставлять параметр, поэтому он устанавливается по умолчанию на основе входных данных (в данном случае база 10). База 1 - это невозможная база чисел, а 3 - недопустимое число в базе 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 

Так что в этом случае вам нужна функция обертки:

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

или с синтаксисом ES2015 +:

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

(В обоих случаях лучше явно указывать основание, parseIntкак показано на рисунке, потому что в противном случае оно угадывает основание на основе входных данных. В некоторых старых браузерах ведущий 0 заставлял его угадать восьмеричное, что, как правило, проблематично. угадайте hex, если строка начинается с 0x.)

Альнитак
источник
25

mapпередает 2-й аргумент, который (во многих случаях) портит parseIntрадикальный параметр.

Если вы используете подчеркивание, вы можете сделать:

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

Или без подчеркивания:

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

philfreo
источник
18

Вы можете решить эту проблему, используя Number в качестве функции iteratee:

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

console.log(a);

Без нового оператора Number можно использовать для преобразования типов. Однако он отличается от parseInt: он не анализирует строку и возвращает NaN, если число не может быть преобразовано. Например:

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

acontell
источник
11

Готов поспорить, что со вторым параметром parseInt, radix, происходит нечто странное. Почему он ломается с использованием Array.map, а не когда вы вызываете его напрямую, я не знаю.

//  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 принимает восьмеричное значение только в том случае, если предоставленная строка начинается с символа 0.
Альнитак
О, да ... это верно. Он пытается «угадать» основание на основе входных данных. Извини за это.
Питер Бейли
3

Вы можете использовать функцию стрелки ES2015 / ES6 и просто передать число в parseInt. Значение по умолчанию для radix будет 10

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

Или вы можете явно указать radix для лучшей читабельности вашего кода.

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

В приведенном выше примере radix явно установлен на 10

Влад Безден
источник
1

другое (рабочее) быстрое исправление:

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

['0', '1', '2', '10', '15', '57'].map(parseInt10);
//[0, 1, 2, 10, 15, 57]
yonatanmn
источник
0

parseIntИМХО следует избегать именно по этой причине. Вы можете обернуть его, чтобы сделать его более безопасным в следующих ситуациях:

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 итерируют аргументы в 1 по умолчанию, чтобы избежать этих ошибок. Лично я нашел эти обходные пути, чтобы создать столько ошибок, сколько они избегают. parseIntЯ думаю, что занесение в черный список в пользу более безопасной реализации - лучший подход.

Дуг Коберн
источник