Why is the number converted to a string (basic Javascript function) - javascript

I have this function (going trough the Eloquent Javascript Tutorial chapter 3):
function absolute(number) {
if (number < 0)
return -number;
else
return number;
}
show(absolute(prompt("Pick a number", "")));
If I run it and enter -3 the output will be 3 as expectet but if I enter just 3 the output will be "3" (with double quotes). I can get around by changing
return number;
to
return Number(number);
but why is that necessary? What am I missing?

prompt() always returns a string, but when you enter a negative number, it is handed to the -number call and implicitly converted to a Number. That doesn't happen if you pass it a positive, and the value received by prompt() is returned directly.
You can, as you discovered, cast it with Number(), or you can use parseInt(number, 10), or you could do -(-number) to flip it negative, then positive again, or more obviously as pointed out in comments, +number. (Don't do --number, which will cast it to a Number then decrement it)

Javascript is not strongly typed.
number comes from the prompt() function, which returns a string.
Since you aren't doing anything to change its type, it remains a string.
-number implicitly converts and returns an actual number.

If you have a string that needs to be converted to a number, please do the following:
var numString = '3';
var num = parseInt(numString);
console.log(num); // 3

JavaScript performs automatic conversion between types. Your incoming "number" is most likely string (you can verify by showing result of typeof(number)).
- does not take "string" as argument, so it will be converted to number first and than negated. You can get the same behavior using unary +: typeof(+ "3") is number when typeof("3") is string.
Same happens for binary - - will convert operands to number. + is more fun as it work with both strings "1"+"2" is "12", but 1+2 is 3.

Related

Numeric conversion in JavaScript [duplicate]

How do parseInt() and Number() behave differently when converting strings to numbers?
Well, they are semantically different, the Number constructor called as a function performs type conversion and parseInt performs parsing, e.g.:
// parsing:
parseInt("20px"); // 20
parseInt("10100", 2); // 20
parseInt("2e1"); // 2
// type conversion
Number("20px"); // NaN
Number("2e1"); // 20, exponential notation
Also parseInt will ignore trailing characters that don't correspond with any digit of the currently used base.
The Number constructor doesn't detect implicit octals, but can detect the explicit octal notation:
Number("010"); // 10
Number("0o10") // 8, explicit octal
parseInt("010"); // 8, implicit octal
parseInt("010", 10); // 10, decimal radix used
And it can handle numbers in hexadecimal notation, just like parseInt:
Number("0xF"); // 15
parseInt("0xF"); //15
In addition, a widely used construct to perform Numeric type conversion, is the Unary + Operator (p. 72), it is equivalent to using the Number constructor as a function:
+"2e1"; // 20
+"0xF"; // 15
+"010"; // 10
typeof parseInt("123") => number
typeof Number("123") => number
typeof new Number("123") => object (Number primitive wrapper object)
first two will give you better performance as it returns a primitive instead of an object.
One minor difference is what they convert of undefined or null,
Number() Or Number(null) Or Number('') // returns 0
while
parseInt() Or parseInt(null) // returns NaN
Summary:
parseInt():
Takes a string as a first argument, the radix (An integer which is the base of a numeral system e.g. decimal 10 or binary 2) as a second argument
The function returns a integer number, if the first character cannot be converted to a number NaN will be returned.
If the parseInt() function encounters a non numerical value, it will cut off the rest of input string and only parse the part until the non numerical value.
If the radix is undefined or 0, JS will assume the following:
If the input string begins with "0x" or "0X", the radix is 16 (hexadecimal), the remainder of the string is parsed into a number.
If the input value begins with a 0 the radix can be either 8 (octal) or 10 (decimal). Which radix is chosen is depending on JS engine implementation. ES5 specifies that 10 should be used then. However, this is not supported by all browsers, therefore always specify radix if your numbers can begin with a 0.
If the input value begins with any number, the radix will be 10
Number():
The Number() constructor can convert any argument input into a number. If the Number() constructor cannot convert the input into a number, NaN will be returned.
The Number() constructor can also handle hexadecimal number, they have to start with 0x.
Example:
console.log(parseInt('0xF', 16)); // 15
// z is no number, it will only evaluate 0xF, therefore 15 is logged
console.log(parseInt('0xFz123', 16));
// because the radix is 10, A is considered a letter not a number (like in Hexadecimal)
// Therefore, A will be cut off the string and 10 is logged
console.log(parseInt('10A', 10)); // 10
// first character isnot a number, therefore parseInt will return NaN
console.log(parseInt('a1213', 10));
console.log('\n');
// start with 0X, therefore Number will interpret it as a hexadecimal value
console.log(Number('0x11'));
// Cannot be converted to a number, NaN will be returned, notice that
// the number constructor will not cut off a non number part like parseInt does
console.log(Number('123A'));
// scientific notation is allowed
console.log(Number('152e-1')); // 15.21
If you are looking for performance then probably best results you'll get with bitwise right shift "10">>0. Also multiply ("10" * 1) or not not (~~"10"). All of them are much faster of Number and parseInt.
They even have "feature" returning 0 for not number argument.
Here are Performance tests.
I found two links of performance compare among several ways of converting string to int.
parseInt(str,10)
parseFloat(str)
str << 0
+str
str*1
str-0
Number(str)
http://jsben.ch/#/zGJHM
http://phrogz.net/js/string_to_number.html
parseInt() -> Parses a number to specified redix.
Number()-> Converts the specified value to its numeric equivalent or NaN if it fails to do so.
Hence for converting some non-numeric value to number we should always use Number() function.
eg.
Number("")//0
parseInt("")//NaN
Number("123")//123
parseInt("123")//123
Number("123ac") //NaN,as it is a non numeric string
parsInt("123ac") //123,it parse decimal number outof string
Number(true)//1
parseInt(true) //NaN
There are various corner case to parseInt() functions as it does redix conversion, hence we should avoid using parseInt() function for coersion purposes.
Now, to check weather the provided value is Numeric or not,we should use nativeisNaN() function
I always use parseInt, but beware of leading zeroes that will force it into octal mode.
It's a good idea to stay away from parseInt and use Number and Math.round unless you need hex or octal. Both can use strings. Why stay away from it?
parseInt(0.001, 10)
0
parseInt(-0.0000000001, 10)
-1
parseInt(0.0000000001, 10)
1
parseInt(4000000000000000000000, 10)
4
It completely butchers really large or really small numbers. Oddly enough it works normally if these inputs are a string.
parseInt("-0.0000000001", 10)
0
parseInt("0.0000000001", 10)
0
parseInt("4000000000000000000000", 10)
4e+21
Instead of risking hard to find bugs with this and the other gotchas people mentioned, I would just avoid parseInt unless you need to parse something other than base 10. Number, Math.round, Math.floor, and .toFixed(0) can all do the same things parseInt can be used for without having these types of bugs.
If you really want or need to use parseInt for some of it's other qualities, never use it to convert floats to ints.
parseInt converts to a integer number, that is, it strips decimals. Number does not convert to integer.
Another way to get the result is to use the ~ operator
For most circumstances
~~someThing === parseInt(something)
but ~~ will return zero for strings that parseInt will accept with trailing other characters or with the number base spec (eg hex) and will also return zero when parseInt returns NaN. Another difference is that ~~ if given a bigint returns a bigint to which you can add another bigint whereas parseInt returns an ordinary floating point number (yes really - it gives exactly the same value as parseFloat) if the bigint is large
However for most circumstances ~~ is 30% faster than parseInt. It is only slower by 10% when something is a floating point represented as a string.
So if the more restricted scope of ~~ fits your need then save the computer time and give yourself less to type

What does a + before a string means in javascript

There is a line of code in angular2.
this.id = +this.route.snapshot.params['id'];
What is the "+" means before "this.route.snapshot.params['id']"?
I also see "+" added before a folder name such as "+detail" in angular2-webpack-starter.
Do they have the same meaning?
Using + in Javascript is a quick way to cast a string to a number as long as the string is already in the form of an integer or float.
+'5000' // yields 5000
+'2.5' // yields 2.5
If the string contains any character that is not an integer (or decimal in the case of a float), this method will return NaN.
+'5n' // yields NaN
+'abcd' // yields NaN
enter image description here
In short if you put + operator in front of string which contains Number
then always get number other than that you will get NaN.

String To Number Confusion

Why does parseInt("-1000-500-75-33") return -1000?
Shouldn't it return the sum of those numbers: -1608
How can I get the string "-1000-500-75-33" to return as the sum of those numbers?
parseInt will try to get a number starting from the beginning of the string.
Since - is a valid character to begin a number with, it parses the string until it finds something invalid. The second - is invalid because no integer can contain an - inside it, only digits. So it stops there and considers the number to be "finished".
Now, if you want to process the expression, you can use eval like so:
eval("-1000-500-75-33")
This will return -1608 as expected.
parseInt will not perform any computations, rather it will try to convert a string into an integer. It returns -1000 because the dash afterwards would not be considered a valid number. If you want to sum all these numbers you could split on the dash, map to Number, then reduce:
var numString = "-1000-500-75-33";
numString.split('-').map(e => Number(e)).reduce((a, b) => a - b);
Try to eval! it's safe here
eval("-1000-500-75-33").toString()
console.log(eval("-1000-500-75-33").toString());
And about type casting: After parsing -1000, which is obviously "negative 1000", It will escape casting as soon as it detect a symbol common between numbers & strings. So parseInt is seeing "-1000-500-75-33" as "-1000NotConvertableString", So left the remaining away, returning -1000 as the result of type-casting.
Since they are in a string, ParseInt does not parse the whole string, just finds the first applicable number from the start & returns it. If the start of the string cannot be parsed, it returns NaN
parseInt("-1000NOT_NUMBER") = -1000
parseInt("test-1000`) = NaN
You have to use eval function to do what you want, that evaluates given string as if it were a command entered into the console;
eval("-1000-500-75-33") = -1608

javascript, parseInt behavior when passing in a float number

I have the following two parseInt() and I am not quite sure why they gave me different results:
alert(parseInt(0.00001)) shows 0;
alert(parseInt(0.00000001)) shows 1
My guess is that since parseInt needs string parameter, it treats 0.00001 as ""+0.00001 which is "0.00001", therefore, the first alert will show 0 after parseInt. For the second statement, ""+0.00000001 will be "1e-8", whose parseInt will be 1. Am I correct?
Thanks
I believe you are correct.
parseInt(0.00001) == parseInt(String(0.00001)) == parseInt('0.00001') ==> 0
parseInt(0.00000001) == parseInt(String(0.00000001)) == parseInt('1e-8') ==> 1
You are correct.
parseInt is intended to get a number from a string. So, if you pass it a number, it first converts it into a string, and then back into a number. After string conversion, parseInt starts at the first number in the string and gives up at the first non-number related character. So "1.e-8" becomes "1"
If you know you are starting with a string, and are just trying to get an Integer value, you can do something like.
Math.round(Number('0.00000001')); // 0
If you know you have a floating point number and not a string...
Math.round(0.00000001); // 0
You can also truncate, ceil(), or floor the number
parseInt takes each character in the first argument (converted to a string) that it recognizes as a number, and as soon as it finds a non-numeric value it ignores that value and the rest of the string. (see MDN second paragraph under "Description")
Therefore it's likely that parseInt(0.00000001) === parseInt(String(0.00000001)) === parseInt("1e-8"), which would only extract the 1 from the string yielding parseInt("1") === 1
However, there's another possibility:
From Mozilla developer network:
parseInt(string, radix);
for the string argument (emphasis added): "The value to parse. If string is not a string, then it is converted to one. Leading whitespace in the string is ignored."
I think this possibility is less likely, since String(0.00000001) does not yield NAN.

Convert an entire String into an Integer in JavaScript

I recently ran into a piece of code very much like this one:
var nHours = parseInt(txtHours);
if( isNaN(nHours)) // Do something
else // Do something else with the value
The developer who wrote this code was under the impression that nHours would either be an integer that exactly matched txtHours or NaN. There are several things wrong with this assumption.
First, the developer left of the radix argument which means input of "09" would result in a value of 0 instead of 9. This issue can be resolved by adding the radix in like so:
var nHours = parseInt(txtHours,10);
if( isNaN(nHours)) // Do something
else // Do something else with the value
Next, input of "1.5" will result in a value of 1 instead of NaN which is not what the developer expected since 1.5 is not an integer. Likewise a value of "1a" will result in a value of 1 instead of NaN.
All of these issues are somewhat understandable since this is one of the most common examples of how to convert a string to an integer and most places don't discuss these cases.
At any rate it got me thinking that I'm not aware of any built in way to get an integer like this. There is Number(txtHours) (or +txtHours) which comes closer but accepts non-integer numbers and will treat null and "" as 0 instead of NaN.
To help the developer out I provided the following function:
function ConvertToInteger(text)
{
var number = Math.floor(+text);
return text && number == text ? number : NaN;
}
This seems to cover all the above issues. Does anyone know of anything wrong with this technique or maybe a simpler way to get the same results?
Here, that's what I came up with:
function integer(x) {
if (typeof x !== "number" && typeof x !== "string" || x === "") {
return NaN;
} else {
x = Number(x);
return x === Math.floor(x) ? x : NaN;
}
}
(Note: I updated this function to saveguard against white-space strings. See below.)
The idea is to only accept arguments which type is either Number or String (but not the empty string value). Then a conversion to Number is done (in case it was a string), and finally its value is compared to the floor() value to determine if the number is a integer or not.
integer(); // NaN
integer(""); // NaN
integer(null); // NaN
integer(true); // NaN
integer(false); // NaN
integer("1a"); // NaN
integer("1.3"); // NaN
integer(1.3); // NaN
integer(7); // 7
However, the NaN value is "misused" here, since floats and strings representing floats result in NaN, and that is technically not true.
Also, note that because of the way strings are converted into numbers, the string argument may have trailing or leading white-space, or leading zeroes:
integer(" 3 "); // 3
integer("0003"); // 3
Another approach...
You can use a regular expression if the input value is a string.
This regexp: /^\s*(\+|-)?\d+\s*$/ will match strings that represent integers.
UPDATED FUNCTION!
function integer(x) {
if ( typeof x === "string" && /^\s*(\+|-)?\d+\s*$/.test(x) ) {
x = Number(x);
}
if ( typeof x === "number" ) {
return x === Math.floor(x) ? x : NaN;
}
return NaN;
}
This version of integer() is more strict as it allows only strings that follow a certain pattern (which is tested with a regexp). It produces the same results as the other integer() function, except that it additionally disregards all white-space strings (as pointed out by #CMS).
Updated again!
I noticed #Zecc's answer and simplified the code a bit... I guess this works, too:
function integer(x) {
if( /^\s*(\+|-)?\d+\s*$/.test(String(x)) ){
return parseInt(x, 10);
}
return Number.NaN;
}
It probaly isn't the fastest solution (in terms of performance), but I like its simplicity :)
Here's my attempt:
function integer(x) {
var n = parseFloat(x); // No need to check typeof x; parseFloat does it for us
if(!isNaN(n) && /^\s*(\+|-)?\d+\s*$/.test(String(x))){
return n;
}
return Number.NaN;
}
I have to credit Šime Vidas for the regex, though I would get there myself.
Edit: I wasn't aware there was a NaN global. I've always used Number.NaN.
Live and learn.
My Solution involves some cheap trick. It based on the fact that bit operators in Javascript convert their operands to integers.
I wasn't quite sure if strings representing integers should work so here are two different solutions.
function integer (number) {
return ~~number == number ? ~~number : NaN;
}
function integer (number) {
return ~~number === number ? ~~number : NaN;
}
The first one will work with both integers as strings, the second one won't.
The bitwise not (~) operator will convert its operand to an integer.
This method fails for integers bigger which can't be represented by the 32bit wide representation of integers (-2147483647 .. 2147483647).
You can first convert a String to an Integer, and then back to a String again. Then check if first and second strings match.
Edit: an example of what I meant:
function cs (stringInt) {
var trimmed = stringInt.trim(); // trim original string
var num = parseInt(trimmed, 10); // convert string to integer
newString = num + ""; // convert newly created integer back to string
console.log(newString); // (works in at least Firefox and Chrome) check what's new string like
return (newString == trimmed); // if they are identical, you can be sure that original string is an integer
}
This function will return true if a string you put in is really an integer. It can be modified if you don't want trimming. Using leading zeroes will fail, but, once again, you can get rid of them in this function if you want. This way, you don't need to mess around with NaN or regex, you can easily check validity of your stringified integer.

Categories

Resources