Array/String lastIndexOf inconsistent results - javascript

Code is self describing
[3, 1, 3].lastIndexOf(3)
2
[3, 1, 3].lastIndexOf(3, undefined)
0 // wtf?
//ok, lets compare with string
'313'.lastIndexOf(3)
2
'313'.lastIndexOf(3, undefined)
2 //wow!

Different algorithms in the spec.
The Array version performs ToInteger() on its second parameter if one was provided, which substitutes 0 for NaN.
If argument fromIndex was passed let n be ToInteger(fromIndex); else let n be len-1.
The String version uses ToNumber() on its second parameter irrespective of whether or not one was provided, which will return NaN without a substitute, so the algorithm manually substitutes Infinity for NaN.
Let numPos be ToNumber(position). (If position is undefined, this step produces the value NaN).
ReturnIfAbrupt(numPos).
If numPos is NaN, let pos be +∞; otherwise, let pos be ToInteger(numPos).
As to why, we can only guess, but the Array version is the newer of the two (ES5), so they must have decided that NaN would be better off being replaced with 0, which makes at least a little sense considering its falsey evaluation (not that it's necessarily relevant).

Related

You should iterate over the elements of the data array and print to the console the number of values in the array that are not NaN in Javasacript

So here is my solution:
let data = [11, null, NaN, 'Hello', 24]
let check = 0;
for (item in data) {
if (isNaN(item) == false) {
check +=1
};
};
console.log(check);
How ever the answer comes five, even though it is 2. I believe isNaN() function gives true or false, so why is it not working?
The in in item in data checks the properties of an object. In an array, the keys are numbers, you have five entries, so you get five times not isNan().
You want to iterate the array, so it should be let item of data
for...in loops over the properties of an object. To loop over an array's elements, use for...of or Array#forEach.
For this specific case, Array#reduce is particularly appropriate.
let data = [11, null, NaN, 'Hello', 24];
let res = data.reduce((a, b) => a + !isNaN(b), 0);
console.log(res);
Your code returns 5 because for...in iterates over the array keys. And the array keys of data are the numbers between and including 0 and 4. Five in total, all of them are numbers.
There are multiple ways to write the code.
Using for...in, use item as an index in data (because this is what it is):
const data = [11, null, NaN, 'Hello', 24]
let check = 0;
for (item in data) {
if (isNaN(data[item]) == false) {
check += 1;
}
}
console.log(check);
Using for...of, item is a value from data and the rest of your code works fine:
const data = [11, null, NaN, 'Hello', 24]
let check = 0;
for (item of data) {
if (isNaN(item) == false) {
check += 1;
}
}
console.log(check);
Using Array.forEach() you get both the index and the value as arguments of the callback function. The value is the first argument, the index is not needed (but it is available as the second argument):
const data = [11, null, NaN, 'Hello', 24]
let check = 0;
data.forEach((item) => {
if (isNaN(item) == false) {
check ++;
}
});
console.log(check);
The callback function used by Array.forEach() changes a variable that is neither received as argument, nor a local variable of the callback function. The code is somewhat confusing this way and it is not properly encapsulated. While this is fine because there is not better option, here it can be done better using Array.reduce(). Array.reduce() seems to be closer to what the program wants to achieve than Array.forEach() (that is just a cleaner way to write the two for loops attended before).
const data = [11, null, NaN, 'Hello', 24]
let check = data.reduce(
(acc, item) => {
if (isNaN(item) == false) {
acc ++;
}
return acc;
},
0
);
console.log(check);
However, Array.reduce() is still a wrong fit. The purpose of the code is to count the numbers from the data array. A more natural way is to not count them but to extract the numbers into a new array using Array.filter(). The array itself tells us how many items it contains by accessing its .length property. There is no need for ifs:
const data = [11, null, NaN, 'Hello', 24]
const check = data.filter((item) => !isNaN(item)).length;
console.log(check);
Other remarks
Do not put semicolons (;) after the close brace } of a code block. The semicolon is a statement separator and a code block can be used wherever a statement can be used. All in all the following piece of code
if (true) {
};
... contains two statements. One is if (true) with an empty code block on the if branch, the other is an empty statement that follows the code block until the semicolon.
It does not have any undesired effect here but it can be the source of hidden bugs difficult to find.
The semicolon is required only after some JavaScript statements. For all the others, a new line works the same as a semicolon. More than that, the JavaScript interpreter is able to insert some missing semicolons automagically.
Attention! I do not say to never use semicolons. Use them where they must be used but be aware that a missing semicolon is either inserted by the interpreter or flagged as a syntax error (and the program does not start) while an extra semicolon in the wrong place can make your program behave in an unexpected way.
Do not use == and !=. They do loose comparisons (f.e. 2 == '2') and sometimes they have surprising results. Always use the strict equality operator (===) and the strict inequality operator (!==). They first check the types of the two operands and do not do confusing conversions to check the values.
x += 1 is usually written x ++. Be aware that, in some contexts, x ++ is has different side effects than ++ x.
There is nothing wrong with += 1, just nobody uses it.
Sometimes, using the logical NOT operator (!) the code becomes more clear. It is abused by many developers that produce "clever" but unclear code but using it here is better than comparing against false.
To me, if (! isNaN(item)) seems easier to understand than if (isNaN(item) === false). It can almost be read: "if item is not NaN" vs. "if item is not NaN is false".
It does not make any difference to the interpreter though. Write the code to be easy to read and understand. The computer does not care.

Why isNaN([3]) is false in JavaScript?

I use Chrome browser 60.x, and test the code isNaN([3]).
The result is false, but I cannot understand it.
[3] is an Array, and it is not empty.
I think [3] is array object, and it is not an number.
Otherwise the result of isNaN(["ABC"]) is true.
And another result of isNaN([1,2,3]) is true.
So I guess javascript engine is force changing array to number which the array has a single element.
Please let me know what is happened isNaN function for array parameter.
ref1: Why is isNaN(null) == false in JS?
ref2: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/isNaN
[EDIT] Thank you everyone for answering.
I understand javascript parsed the value implicitly before comparison.
And I found a useful link while reading Nina Scholz's answer.
The comparison table : http://dorey.github.io/JavaScript-Equality-Table/
When you use isNaN it tries to parse your input value into the number and then checks if it is NaN or not.
See some examples.
For an array with one item, the Number() function returns an object, which actually hold the first item as a value (see console.log). For many items it returns NaN, so you get isNaN's result -> true.
const a = new Number([3]);
console.log(`a is ${a}`);
console.log(`isNaN(a) - ${isNaN(a)}`);
const b = new Number([3,4,5]);
console.log(`b is ${b}`);
console.log(`isNaN(b) - ${isNaN(b)}`);
According to the rules for equality comparisons and sameness, the array is converted, first with toString and then to a number for checking.
console.log(isNaN([3]));
console.log(isNaN('3')); // convert with toString to a primitive
console.log(isNaN(3)); // convert to number
Javascript works in the following way: It tries to convert the object to integer if the argument of the function is expected to be an integer. For example running
x = increment([3])
will result in x = 4. Therefore running
isNan([3])
will result in false, since [3] gets converted to 3 and 3 is a number. Similarly, ["ABC"] cannot get converted to integer, therefore isNaN(["ABC"]) = true. Also, javascript fails to convert [1,2,3] to a number, since there are three numbers in array, therefore
isNaN([1,2,3]) = true
Because, as a shorthand in Javascript,, arrays containing only one number are considered castable to the number it contains. You can see this behavior with ==
3 == [3] // returns true
Additionaly, that's also why [0] is a falsy value, whereas [n] with n not zero is a truthy value, an horrible potential source of bugs.
[0] == false // returns true
[1] == false // returns false
So as we know
isNaN([3]) // returns false
A workaround to test number vs array with number
Number([3]) === [3] //returns false
Number(3) === 3 // returns true
Remember that === means matching the values and type

javascript while loop, positive number given as a condition

I know that in javascript a 1 evaluates as true, while a 0 evaluates as false.
My question is regarding the while loop in javascript, in the format as follows:
while (condition) {
code block to be executed
}
If I pass a single integer as a condition for the while loop, how does the code function? More specifically, here is the code I was working on before posting this question:
function chunkArrayInGroups(arr, size) {
var newArr = [];
while (arr.length) {
newArr.push(arr.splice(0,size));
}
return newArr;
}
chunkArrayInGroups([0, 1, 2, 3, 4, 5], 2);
and it seems to be working perfectly fine, returning the newArr the way I need it to by dividing the original array (arr) the amount of times required (size) and pushing that to the newArr.
How does the condition arr.length evaluate? My assumption is that it evaluates as true as long as it is not zero, i.e. a nonzero number, despite it not being a comparison such as i < 2.
This exercise came from freeCodeCamp:
Chunky Monkey
Any number that is not 0 evaluates to true.
0 evaluates to false.
So, your code will run until arr.length is equal to 0 - it is the same as saying while (arr.length!=0)
if you're evaluating array's length property, it will be true until array is not empty ( length is not 0 ). When the array is empty ( length is 0) evaluation will return false.
Yes
Yes, you are absolutely right, it evaluates as true whilst arr has a length because arr.length is > 0 so is considered true. It is a neat feature of javascript however it is not that readable (as you have found) and can therefore be confusing to anyone not used to it. However if you are coding for yourself, this is a clever little function!

Why does `isFinite(null) === true`?

The following are examples that make sense to me.
isFinite(5) // true - makes sense to me, it is a number and it is finite
typeof 5 // "number"
isFinite(Infinity) // false - makes sense for logical reasons
typeof Infinity // "number"
isFinite(document) // false - makes sense as well, it's not even a number
typeof document // "object"
The following is where I get confused.
isFinite(null) // true - Wait what? Other non-number objects returned false. I see no reason?
typeof null // "object"
I just don't see the reasoning behind this.
What I'd like is the most low-level answer possible.
I think null is being converted to 0, why? What other impacts does this have?
The ECMAScript spec (5.1) defines isFinite to act as such:
isFinite (number)
Returns false if the argument coerces to NaN, +∞, or −∞, and otherwise returns true.
If ToNumber(number) is NaN, +∞, or −∞, return false.
Otherwise, return true.
In other words, isFinite is calling ToNumber on whatever's passed in, and then comparing it to either pos/neg infinity or NaN.
In JavaScript (note the use of != instead of the more common !==, causing the type cast):
function isFinite(someInput) {
return !isNaN(someInput) &&
someInput != Number.POSITIVE_INFINITY &&
someInput != Number.NEGATIVE_INFINITY;
}
(As noted in the comments below, someInput != NaN is not needed, as NaN is defined to not be equivalent to everything, including itself.)
Now, why is null converted to zero (as opposed to undefined)? As TylerH says in the comments, null means that a value exists, but is empty. The mathematical representation of this is 0. undefined means that there isn't a value there, so we get NaN when trying to call ToNumber on it.
http://www.ecma-international.org/ecma-262/5.1/#sec-15.1.2.5
However, ECMAScript 6 is bringing along a non-converting isFinite as a property of Number. Douglas Crockford suggested it here: http://wiki.ecmascript.org/doku.php?id=harmony:number.isfinite
From MDN:
The global isFinite() function determines whether the passed value is
a finite number. If needed, the parameter is first converted to a
number.
So, it's converted to a number...
isFinite(null)
isFinite(+null) //convert to a number
isFinite(0) // true because +null or Number(null) = 0
The spec says that the global isFinite() method will forcibly convert the parameter to a number.
You could, however, use (at your own risk) the EcmaScript 6 spec's Number.isFinite() which doesn't perform this conversion.
Number.isFinite(null) // false
Or, like lodash and underscore do it...
var _.isFinite = function(obj) {
return isFinite(obj) && !isNaN(parseFloat(obj));
};
isFinite calls ToNumber on its argument. So
> Number(null)
0
> Number(document)
NaN
> isFinite(0)
true
> isFinite(NaN)
false
> isFinite(null)
true
> isFinite(document)
false
Because, if you say
Number(null) === 0 which is finite
See To Number Conversions
Which says that, for argument type null result is +0
isFinite typecasts it's argument to a number, if it's not already a number. Essentially you have isFinite(Number(null)) and Number(null) === 0. Which is finite.
Beside null, you find these examples interesting too:
alert(isFinite(' ')); //true
alert(isFinite('')); //true
alert(isFinite(null)); //true
alert(isFinite(!undefined)); //true
In JavaScript implicit conversion take place, This conversion try to convert bool to integer when comparing numbers with boolean, or number to string when comparing string with numbers. If you treat any data-type as number it implicitly converted to number so all above cases return zero which is finite. See Here
If you try Number(undefined) it give you a NaN on negate this would produce a 1 which is finite.

What does !!~ do? [duplicate]

This question already exists:
What does `!!~` mean in javascript? [duplicate]
Closed 8 years ago.
I was reading this blog post which mentioned using:
!!~
I have no idea what this does? at first I thought it would give an error, but the code below does run:
var _sessions = [
"_SID_1",
"_SID_2",
"_SID_3",
"_SID_4"
];
if(!!~_sessions.indexOf("_SID_5")) {
console.log('found');
} else {
console.log('!found');
}
output:
node test.js
!found
~ is the bitwise not operator. It inverts the bits of its operand. ! is the logical not operator. The bitwise not operator will return 0 when applied to -1, which is what indexOf returns when the value is not found in the array. Since 0 evaluates to false, doubly negating it will simply return false (a boolean value, rather than a numeric one):
var index = _sessions.indexOf("_SID_5");
console.log(~index); // 0
console.log(!~index); // true
console.log(!!~index); //false
The bitwise not operator will return a value less than 0 for any other possible value returned by indexOf. Since any other value will evaluate to true, it's just a shorthand method (kind of... they are both the same number of characters!) of checking whether an element exists in an array, rather than explicitly comparing with -1:
if (_sessions.indexOf("_SID_5") > -1) {
// This would work the same way
}
Update
With regards to the performance of this, it appears (in Chrome at least) to be marginally slower than the more common comparison with -1 (which itself is marginally slower than a comparison with 0).
Here's a test case and here's the results:
Update 2
In fact, the code in your question can be shortened, which may have been what the author was attempting to do. You can simply remove the !!, since the ~ will always result in 0 or below (and 0 is the only value that will evaluate to false):
if (~_sessions.indexOf("_SID_5")) {
// This works too
}
However, in a slightly different situation it could make sense to add in the ! operators. If you were to store the result of the bitwise operator in a variable, it would be a numeric value. By applying the logical not operator, you get a boolean value (and applying it again ensures you get the correct boolean value). If for some reason you require a boolean value over a numeric one, it makes a little bit more sense (but you can still just use the normal comparison with -1 or 0):
var inArray = !!~_sessions.indexOf("_SID_5");
console.log(typeof inArray); // boolean
Donald Knuth: "[...] premature optimization is the root of all evil"
For the sake of readability: please use
.indexOf !== -1
This explains it well:
The tilde operator in Javascript
Mixing the two NOT operators together can produce some interesting results:
!~(-2) = false
!~(-1) = true
!~(0) = false
!~(1) = false
!~(2) = false
So this just checks if the value equals -1 or not, and indexOf returns -1 if it does not find a match

Categories