I was perusing the underscore.js annotated source when I encountered this:
if (obj.length === +obj.length) {...}
I now know from this stackoverflow question that the plus sign (+) operator returns the numeric representation of the object.
That said, obj.length returns a number. When would obj.length not be equal to +obj.length?
The === operator does not make any typecast when it checks, so different types of data will immediately return false even if '5' == 5. The + as you said typecasts the object into number. If you typecast a number into a number, it is still a number, so you basically check if your object.length exists and is a number. Values like undefined, NaN, null, string and others will return false. You are not sure what happens with obj, so you have to check...
When, for example:
var obj = {
0: 'first',
length: '1'
};
alert(obj.length === +obj.length);
Underscore's each is a generic, therefore can work with other objects other than an array. Just like ECMA5 forEach
The forEach function is intentionally generic; it does not require that its this value be an Array object. Therefore it can be transferred to other kinds of objects for use as a method. Whether the forEach function can be applied successfully to a host object is implementation-dependent.
So underscore are checking the validity of an object's length property. And they deem an object arrayLike, for that method of iteration, only if the object's length returns a number which is not NaN, and is certainly not a string. So in my above example, obj would fall through to their keys iteration, if there is no native/polyfilled forEach.
Related
This question already has answers here:
How to check if an array is empty or exists?
(23 answers)
Closed 2 years ago.
What's the best way to check if an array is empty or does not exist?
Something like this?
if(array.length < 1 || array == undefined){
//empty
}
You want to do the check for undefined first. If you do it the other way round, it will generate an error if the array is undefined.
if (array === undefined || array.length == 0) {
// array does not exist or is empty
}
Update
This answer is getting a fair amount of attention, so I'd like to point out that my original answer, more than anything else, addressed the wrong order of the conditions being evaluated in the question. In this sense, it fails to address several scenarios, such as null values, other types of objects with a length property, etc. It is also not very idiomatic JavaScript.
The foolproof approach
Taking some inspiration from the comments, below is what I currently consider to be the foolproof way to check whether an array is empty or does not exist. It also takes into account that the variable might not refer to an array, but to some other type of object with a length property.
if (!Array.isArray(array) || !array.length) {
// array does not exist, is not an array, or is empty
// ⇒ do not attempt to process array
}
To break it down:
Array.isArray(), unsurprisingly, checks whether its argument is an array. This weeds out values like null, undefined and anything else that is not an array.
Note that this will also eliminate array-like objects, such as the arguments object and DOM NodeList objects. Depending on your situation, this might not be the behavior you're after.
The array.length condition checks whether the variable's length property evaluates to a truthy value. Because the previous condition already established that we are indeed dealing with an array, more strict comparisons like array.length != 0 or array.length !== 0 are not required here.
The pragmatic approach
In a lot of cases, the above might seem like overkill. Maybe you're using a higher order language like TypeScript that does most of the type-checking for you at compile-time, or you really don't care whether the object is actually an array, or just array-like.
In those cases, I tend to go for the following, more idiomatic JavaScript:
if (!array || !array.length) {
// array or array.length are falsy
// ⇒ do not attempt to process array
}
Or, more frequently, its inverse:
if (array && array.length) {
// array and array.length are truthy
// ⇒ probably OK to process array
}
With the introduction of the optional chaining operator (Elvis operator) in ECMAScript 2020, this can be shortened even further:
if (!array?.length) {
// array or array.length are falsy
// ⇒ do not attempt to process array
}
Or the opposite:
if (array?.length) {
// array and array.length are truthy
// ⇒ probably OK to process array
}
We all know Strings somewhat behaves like an Array.You can even apply some of the array methods to it and take benefits, like the below example.
[].filter.call('abcdef',function(val){
return val<'e';
});
Also,
var a='xyz';
I can access first element using a[0] or I can call a.length like an Array
My question is why String behaves like an Array. and if it does, why do I get false below when I check if it is an instance of Array. Is String Array-like?
'a' instanceof Array
All that Array.prototype.filter really requires is that the variable being iterated over has a length property, and that the variable has numeric-indexed values. See (part of) the polyfill:
var len = this.length >>> 0,
res = new Array(len), // preallocate array
t = this, c = 0, i = -1;
if (thisArg === undefined){
while (++i !== len){
// checks to see if the key was set
if (i in this){
if (func(t[i], i, t)){
res[c++] = t[i];
}
}
}
}
Strings fulfill this condition - strings have a length property, and numeric indicies accessed on the string resolve to individual characters.
But you can do the same thing with arbitrary objects:
const obj = {
0: 'foo',
1: 'bar',
length: 2
};
const result = [].filter.call(obj, val => val.startsWith('f'));
console.log(result);
You could say that obj is array-like as well, since it has a length property and numeric indicies. Most array methods like .filter, .reduce, etc can be .called on array-like objects, even if those objects aren't actual arrays.
(Technically, you can also call array methods on non-array-like objects too, it just won't do anything useful - no iterations may be performed)
To use instanceof you need to create an instance of an Object and a is not an instance of one. It is a primitive or also known as string literal:
String literals (denoted by double or single quotes) and strings
returned from String calls in a non-constructor context (i.e., without
using the new keyword) are primitive strings. JavaScript automatically
converts primitives to String objects, so that it's possible to use
String object methods for primitive strings. In contexts where a
method is to be invoked on a primitive string or a property lookup
occurs, JavaScript will automatically wrap the string primitive and
call the method or perform the property lookup.
For example:
let foo = 'abc' // primitive
let boo = new String() // object
console.log(foo instanceof String) // false
console.log(boo instanceof String) // true
console.log(typeof foo) // 'string' <-- notice not String
console.log(typeof boo) // object
This is simply due to:
The instanceof operator tests the presence of constructor.prototype in
object's prototype chain
But as we explained above we are dealing with string literals which are created without a constructor call (new keyword) and only boxed for our convenience when operating on them. They are not actual instances of String and thus instanceof returns false.
The reason you can use Array.filter on the string primitive is simply due to the fact that it was boxed for you to a String from where it got the length property at the time of execution.
For example in the case of V8 engine string primitive is parsed/boxed to String and a String to a StringObject. Notice they are actually different instances.
Array.filter only cares about that length property and numeric indicies as pointed nicely by CertainPerformance which are provided by the boxing to String. Example:
console.log(Object.getOwnPropertyNames('a')) // ["0", "length"]
However String is not StringObject and thus instanceof would return false.
I am confused about placement of operators:
Given:
var a = [0, 1, 2];
So far as I can tell, each of the following is correct:
var len = a.length;
var lastElt = a.pop();
var str = String(a);
var typeStr = typeof a;
Is there an easy way to remember/think about whether the operator goes before or after the operand and whether it requires parentheses? Or is it simply rote memorization?
Thanks.
Let's go:
case #1
var len = a.length;
In this case you're calling the method lenght of the array store in the variable a.
case #2
var lastElt = a.pop();
As before, here you're calling the method pop of the array.
case #3
var str = String(a);
You aren't not calling any array's method. Here you're casting the array. i.e. you are stringifying the array.
case #4
var typeStr = typeof a;
Here you also aren't calling any array's method. You are calling the typeof method of the window object and passing the array a as an argument.
As you can see. In the first two cases, you are calling array's methods. In the last two cases, you are calling window object's methods and passing your array as an argument.
Your list there mostly consists of things that are not operators:
length is a property belonging to Array objects. It returns the number of items in the Array.
pop() is a method belonging to Array objects. It removes the last item from the Array and retuns it.
String() is a global object which is the constructor for a String object. It takes any object as a parameter and converts it to a String
typeof is an operator. It returns a string that indicates the type of the subsequent operand.
For more reference, here is some information about JavaScript operators.
I highly recommend searching Google for some beginning JavaScript tutorials so you can learn the basic concepts.
Thanks for all the prompt replies. In reply to the question 'What do you mean by "operator" here?' I come from a background of mathematics and (lately) c programming. By "operator" I wanted to speak abstractly about anything that mapped its argument to something useful, using it to include methods, properties, etc., without enumerating them. My attempt to abstract these is probably the source of my confusion. I now understand that what is required is rote memorization, something like declensions in Latin ;-).
Operators refer to +,=*,/ etc.
I don't think there is an easy way to remember. If it is a 'property', there will be no parentheses. If it is a function, there will be parentheses. I always remember that length is a property of an array, and that push and pop are actions you can do to the array. Type casting always starts with the 'type' capitalized followed by what you want casted in the parenthesis. typeof is just a weird one.
I've got a simple array cars of Parse models of class Cars
When I do
var uniqCars = _.uniq(cars);
it doesn't work. uniqCars is exact same as cars. The length of cars is 5 and length of uniqCars is 5 (when it should be 2).
However, when I do:
var uniqCars = _.uniq(cars,
function (c) {
return c.id;
});
It works. My question is, why doesn't it work with the former and works with the latter? Why do I have to be so verbose? Is this an issue with Parse or underscore?
why doesn't it work with the former
because, if you don't pass the comparator function, it by default uses === operator to compare the objects. Quoting the _.uniq documentation,
Produces a duplicate-free version of the array, using === to test object equality. ... If you want to compute unique items based on a transformation, pass an iteratee function
When you use === (Strict Equality operator), no two objects will be the same unless they are one and the same object or same sequence of characters forming a string. For example,
console.assert(({} === {}) === false);
var obj = {};
console.assert(obj === obj);
console.assert("ab" === "a" + "b")
console.assert("ab" === 'a' + "b")
So, it is not specific to Parse but it is the expected behaviour in JavaScript.
The comparisons are made using strict equality. Unless there are multiple references to the same object in the array, they are not going to be strictly equal.
Produces a duplicate-free version of the array, using === to test object equality. In particular only the first occurence of each value is kept. If you know in advance that the array is sorted, passing true for isSorted will run a much faster algorithm. If you want to compute unique items based on a transformation, pass an iteratee function.
This question already has answers here:
How to check if an array is empty or exists?
(23 answers)
Closed 2 years ago.
What's the best way to check if an array is empty or does not exist?
Something like this?
if(array.length < 1 || array == undefined){
//empty
}
You want to do the check for undefined first. If you do it the other way round, it will generate an error if the array is undefined.
if (array === undefined || array.length == 0) {
// array does not exist or is empty
}
Update
This answer is getting a fair amount of attention, so I'd like to point out that my original answer, more than anything else, addressed the wrong order of the conditions being evaluated in the question. In this sense, it fails to address several scenarios, such as null values, other types of objects with a length property, etc. It is also not very idiomatic JavaScript.
The foolproof approach
Taking some inspiration from the comments, below is what I currently consider to be the foolproof way to check whether an array is empty or does not exist. It also takes into account that the variable might not refer to an array, but to some other type of object with a length property.
if (!Array.isArray(array) || !array.length) {
// array does not exist, is not an array, or is empty
// ⇒ do not attempt to process array
}
To break it down:
Array.isArray(), unsurprisingly, checks whether its argument is an array. This weeds out values like null, undefined and anything else that is not an array.
Note that this will also eliminate array-like objects, such as the arguments object and DOM NodeList objects. Depending on your situation, this might not be the behavior you're after.
The array.length condition checks whether the variable's length property evaluates to a truthy value. Because the previous condition already established that we are indeed dealing with an array, more strict comparisons like array.length != 0 or array.length !== 0 are not required here.
The pragmatic approach
In a lot of cases, the above might seem like overkill. Maybe you're using a higher order language like TypeScript that does most of the type-checking for you at compile-time, or you really don't care whether the object is actually an array, or just array-like.
In those cases, I tend to go for the following, more idiomatic JavaScript:
if (!array || !array.length) {
// array or array.length are falsy
// ⇒ do not attempt to process array
}
Or, more frequently, its inverse:
if (array && array.length) {
// array and array.length are truthy
// ⇒ probably OK to process array
}
With the introduction of the optional chaining operator (Elvis operator) in ECMAScript 2020, this can be shortened even further:
if (!array?.length) {
// array or array.length are falsy
// ⇒ do not attempt to process array
}
Or the opposite:
if (array?.length) {
// array and array.length are truthy
// ⇒ probably OK to process array
}