Validate if a given string is numeric.
Some examples:
"0"
=> true
" 0.1 "
=> true
"abc"
=> false
"1 a"
=> false
"2e10"
=> true
Note: It is intended for the problem statement to be ambiguous. You should gather all requirements up front before implementing one.
Update (2015-02-10): The signature of the
C++
function had been updated. If you still see your function
signature accepts a const char *
argument, please click the
reload button to reset your code definition.
JavaScript specific solutions:
Math.abs
will first convert the argument to number.Math.abs(' ') === 0
.
/**
isNaN
will first convert the argument to number.isNaN(' ') === false
.
/**
General solution. Take a look at the ECMA Spec.
Similary, we can define our own syntax, which requires a few changes:
SignedDecimalLiteral::
DecimalLiteral
+ DecimalLiteral
- DecimalLiteral
DecimalLiteral::
DecimalDigits . [DecimalDigits] [ExponentPart]
. DecimalDigits [ExponentPart]
DecimalDigits [ExponentPart]
DecimalDigits::
DecimalDigit
DecimalDigits DecimalDigit
DecimalDigit:: one of
0123456789
ExponentPart::
ExponentIndicator SignedInteger
ExponentIndicator::one of
eE
SignedInteger::
DecimalDigits
+ DecimalDigits
- DecimalDigits
Now implement the parser. It is much easier now because we have a clear mental map of the syntax.
/**
* @param {string} s
* @return {boolean}
*/
var isNumber = function(s) {
let start = 0
while (s[start] === ' ') {
start++
}
if (s[start] === '+' || s[start] === '-') {
start++
}
let nextIndex = parseDecimalLiteral(s, start)
while (s[nextIndex] === ' ') {
nextIndex++
}
return nextIndex === s.length
}
/**
* @param {string} s
* @param {number} start - start index
* @return {number} next index, -1 means error
*/
function parseDecimalLiteral (s, start) {
let nextIndex = -1
if (s[start] === '.') {
nextIndex = parseDecimalDigits(s, start + 1)
if (nextIndex === -1) { return -1 }
} else {
nextIndex = parseDecimalDigits(s, start)
if (nextIndex === -1) { return -1 }
if (s[nextIndex] === '.') {
const optNextIndex = parseDecimalDigits(s, ++nextIndex)
if (optNextIndex !== -1) {
nextIndex = optNextIndex
}
}
}
const optNextIndex = parseExponentPart(s, nextIndex)
return optNextIndex === -1 ? nextIndex : optNextIndex
}
/**
* @param {string} s
* @param {number} start - start index
* @return {number} next index, -1 means error
*/
function parseDecimalDigits (s, start) {
if (start === s.length) { return -1 }
for (let i = start; i < s.length; i++) {
const digit = s.charCodeAt(i) - 48
if (!(digit >= 0 && digit <= 9)) {
return i === start ? -1 : i
}
}
return s.length
}
/**
* @param {string} s
* @param {number} start - start index
* @return {number} next index, -1 means error
*/
function parseDecimalIntegerLiteral (s, start) {
if (start === s.length) { return -1 }
let nextIndex = start
if (s[start] === '0') {
nextIndex++
}
const digit = s.charCodeAt(nextIndex) - 48
if (!(digit > 0 && digit <= 9)) {
return nextIndex === start ? -1 : nextIndex
}
nextIndex++
const optNextIndex = parseDecimalDigits (s, nextIndex)
return optNextIndex === -1 ? nextIndex : optNextIndex
}
/**
* @param {string} s
* @param {number} start - start index
* @return {number} next index, -1 means error
*/
function parseExponentPart (s, start) {
if (s[start] !== 'e' && s[start] !== 'E') {
return -1
}
let nextIndex = start + 1
if (s[nextIndex] === '+' || s[nextIndex] === '-') {
nextIndex++
}
return parseDecimalDigits(s, nextIndex)
}
Template generated via Leetmark.