Чому парсеринг дає NaN за допомогою масиву # map?


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
Для ледачих: використовуйте, .map(parseFloat)оскільки він бере один параметр.

63
Або використовувати .map(Number).
Микола

2
ви можете arr.map (Math.floor), якщо ви хочете, щоб цілі особи були без ручної функції.
dandavis

@Nikolai user669677 чудові пропозиції! Я б підкреслив це у відповіді
BiAiB

хтось може пояснити, чому parseInt правильно аналізує перше число та робить помилку, окрім першого індексу
bawa g

Відповіді:


478

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

З тієї ж сторінки Mozilla, яку ви пов’язали:

Зворотний виклик викликається трьома аргументами: значенням елемента, індексом елемента та об'єктом масиву, що пересувається. "

Отже, якщо ви викликаєте функцію, 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 змусив його вгадати восьмерику, що, як правило, було проблематичним. Це все одно буде вгадайте шістнадцятковий, якщо рядок починається з 0x.


25

mapпередає другий аргумент, який (у багатьох випадках) parseIntпсує параметр radix.

Якщо ви використовуєте підкреслення, ви можете зробити:

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

Або без підкреслення:

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


18

Ви можете вирішити цю проблему, використовуючи номер як функцію ітерації:

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

console.log(a);

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

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


11

Я збираюся зробити ставку, що це щось дивне відбувається з другим параметром parseInt, радіусом. Чому він порушується з використанням 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 і просто передати номер аналізу. За замовчуванням для radix буде 10

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

Або ви можете чітко вказати radix для кращої читабельності вашого коду.

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

У наведеному вище прикладі радіакс явно встановлений на 10


1

ще одне (робоче) швидке виправлення:

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

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

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 за замовчуванням обмежує аргументи iteratee до 1, щоб уникнути цих дозволів. Особисто я знайшов ці обхідні шляхи, щоб створити стільки помилок, скільки вони уникають. Чорний список parseIntна користь більш безпечного впровадження - я думаю, кращий підхід.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.