What is the difference between includes and hasOwnProperty for searching an element in an Array in JS in terms of time and space complexity?
Both can be used to find whether an element exists in an Array or not.
hasOwnProperty returns a boolean value indicating whether the object on which you are calling it has a property with the name of the argument and as everyone The includes() method determines whether a string contains the characters of a specified string. include method returns true if the string contains the characters, and false if not. Also, it's the case sensitive.
and at the end, I think the question is based on a false assumption.
Both can be used to find whether an element exists in an Array or not.
Incorrect. includes checks if any of the elements in the array match the argument. hasOwnProperty checks to see if the Array has a property that matches the argument
$ const array = ['test', 'another'];
$ array.includes('test')
true
$ array.hasOwnProperty('test')
false
$ array.includes('length')
false
$ array.hasOwnProperty('length')
true
You can see that includes and hasOwnProperty do not return the same result, so it is pointless to compare efficiency.
includes is checking if your array contains specified value, while hasOwnProperty checks if your array has specified property name in its __proto__:
const arr = [1, 2, 3];
arr.prop = 'Some prop';
arr.includes(2); // -> true
arr.hasOwnProperty(2); // -> false
arr.includes('prop'); // -> false
arr.hasOwnProperty('prop'); // -> true
Related
In an answer for this question (which otherwise I can fully understand/etc), there'a this curious quite:
From the spec, 15.4.4.11 :
Because non-existent property values always compare greater than
undefined property values, and undefined always compares greater than
any other value, undefined property values always sort to the end of
the result, followed by non-existent property values.
I've checked in the latest version available now and it's "note 1" at the end of sort spec, and it's basically the same as it was when that answer from 2011 was written.
Regarding undefined property values always sort to the end of the result, followed by non-existent property values -- how can it be? what are "non-existent property values"(*)? if we write a.foo and a doesn't have such property, we'll get undefined, so how can it be differentiated?
The sort is called either without any parameter, or with a comparer-style function, and in the latter case, it's our function, and we're bound to read the non-existent property and get undefined.. The sort can't inspect the object's keys for us to decide whever an inspected object has a property or not (in contrast to i.e. certain underscore/lodash helpers where you define a 'path' like i.e. pluck or get). I just dont see how we could trigger this "non-existent property values" case at all.
(*) I've found something that looks like a definition of this term here:
A non-existent property is a property that does not exist as an own property on a non-extensible target.
(...)
If the target is non-extensible and P is non-existent, then all future
calls to [[GetOwnProperty]] (P) on the target must describe P as
non-existent (i.e. [[GetOwnProperty]] (P) must return undefined).
This must-describe-as-nonexistent and must-return-undefined seem to support my doubt.
I've also noticed that the pseudo-code for SortIndexedProperties (used to define sort) actually contains bits like 3.b. Let kPresent be ? HasProperty(obj, Pk).. So maybe that non-existent property part in sort spec meant to cover some case like the array being mutated by the comparer function and certain keys are removed from it?
A non-existent property on an array will be "included" when sorting only if the array is sparse. Here's an example, look at the comments:
const arr = []; // 0-length array
arr[2] = 'foo'; // Turns into 3-length array. Does not have own properties 0 or 1
arr.sort();
console.log(arr[0]); // Sort result includes a value from defined property
console.log(arr.hasOwnProperty(1)); // But not from the sparse elements
console.log(arr.length); // But the array will have the same length as originally
As you can see, the properties that didn't exist on the original sparse array are "sorted" to the end of the array, and don't exist on the resulting sorted array either, except for the .length of the sorted array. It started out as an array with a length of 3 and only one own-property, and ended up as an array with a length of 3 and only one own-property (but with that own-property at a different index).
if we write a.foo and a doesn't have such property, we'll get undefined, so how can it be differentiated?
It's equivalent to a .hasOwnProperty check:
const obj1 = {};
const obj2 = { foo: undefined };
console.log(obj1.hasOwnProperty('foo'));
console.log(obj2.hasOwnProperty('foo'));
const arr1 = ['abc'];
const arr2 = [, 'def']; // sparse array
console.log(arr1.hasOwnProperty(0));
console.log(arr2.hasOwnProperty(0));
Best approach to all of this - never use sparse arrays to begin with, they're too confusing.
I have some code that is comparing a string against values in an array:
var blacklistedSites = ['https://www.google.com/_/chrome/newtab?espv=2&ie=UTF-8'];
//Returns true if the current site is blacklisted, false otherwise
function isBlacklistedSite(url) {
console.log('Site is ' + url);
blacklistedSites.forEach(function(entry) {
console.log('testing ' + entry);
if (entry == document.URL) {
return true;
}
});
return false;
}
console.log(isBlacklistedSite('https://www.google.com/_/chrome/newtab?espv=2&ie=UTF-8'));
This outputs:
Site is https://www.google.com/_/chrome/newtab?espv=2&ie=UTF-8
testing https://www.google.com/_/chrome/newtab?espv=2&ie=UTF-8
false
Why does isBlacklistedSite() not detect a match?
The reason your code doesn't work is that your:
return true;
effectively does nothing. It just returns from the forEach function, which happens anyway regardless of there being a match, or not. Your return true; does not return from your isBlacklistedSite() function. Your isBlacklistedSite() function always exits with:
return false;
While you could do this using .forEach(), it is a poor choice. The .forEach() method always iterates over every member of the array, regardless of any return value you provide in the forEach function. You would only use it if you were also doing some other operation on every element of the array at the same time. Even then, it might be better to separate out the two different tasks. If you did use it, you would have to keep the detection in a variable defined external to the .forEach(function(){....
Use .indexOf() to test an exact match to an array element
If you want to test for exact matches of Array elements, you can use .indexOf() and test for a value > -1.
For instance:
var blacklistedSites = ['https://www.google.com/_/chrome/newtab?espv=2&ie=UTF-8'];
function isBlacklistedSite(url) {
console.log('Site is ' + url);
return blacklistedSites.indexOf(url) > -1;
}
//Test with a known matching value.
console.log(isBlacklistedSite(blacklistedSites[0]));
//Test with a known failing value.
console.log(isBlacklistedSite('foo'));
If you need a more complex test, you can use .some()
var blacklistedSitesRegExes = [/(?:https?:)?\/\/[^/]*www\.google\.com\/.*espv=2/];
function isBlacklistedSite(url) {
console.log('Site is ' + url);
return blacklistedSitesRegExes.some(function(regex){
regex.lastIndex = 0; //Prevent contamination from prior tests
return regex.test(url);
});
}
//Test with a known matching value.
console.log(isBlacklistedSite('https://www.google.com/_/chrome/newtab?espv=2&ie=UTF-8'));
//Test with a known failing value.
console.log(isBlacklistedSite('foo'));
With limited availability: .includes() (not for production code)
.includes() does exactly what you want (return a Boolean true/false for an exact match). However, it is not as generally available as .indexOf(). It is recommended not to use it in production code. For Arrays, it does not add much benefit over .indexOf(url) > -1.
Additional methods
There are many additional methods available to Arrays which could be used to determine that you have a match. What you use will depend on your specific needs. As always, you should be mindful of compatibility issues for any method you choose to use. Some of the available methods are (text from MDN):
Array.prototype.every()
Returns true if every element in this array satisfies the provided testing function.
Array.prototype.filter()
Creates a new array with all of the elements of this array for which the provided filtering function returns true.
Array.prototype.find() (no IE)
Returns the found value in the array, if an element in the array satisfies the provided testing function or undefined if not found.
Array.prototype.findIndex() (no IE)
Returns the found index in the array, if an element in the array satisfies the provided testing function or -1 if not found.
Array.prototype.includes() (compatibility issues, including no IE)
Determines whether an array contains a certain element, returning true or false as appropriate.
Array.prototype.indexOf()
Returns the first (least) index of an element within the array equal to the specified value, or -1 if none is found.
Array.prototype.lastIndexOf()
Returns the last (greatest) index of an element within the array equal to the specified value, or -1 if none is found.
Array.prototype.some()
Returns true if at least one element in this array satisfies the provided testing function.
Could be used, but not very appropriate:
Array.prototype.forEach()
Calls a function for each element in the array.
Array.prototype.map()
Creates a new array with the results of calling a provided function on every element in this array.
Array.prototype.reduce()
Apply a function against an accumulator and each value of the array (from left-to-right) as to reduce it to a single value.
Array.prototype.reduceRight()
Apply a function against an accumulator and each value of the array (from right-to-left) as to reduce it to a single value.
I have some wtfjs code:
var a = [,];
alert(a.indexOf(a[0]));
a.indexOf(a[0]) returns -1. The main point in this example is difference between uninitialized and undefined values:
a contains one not initialized element.
a[0] return undefined.
a don't contains the undefined value. So a.indexOf(a[0]) === -1 is true.
But where I can find the explanation why a[0] return undefined? What internal method is calling?
P.S. Undefined is the javascript primitive type. Uninitialized means the value that don't have any javascript type, but there is no such primitive type in javascript.
The ES5 spec tells us the following of array initialisers:
Elided array elements are not defined.
Note that they are not defined. That's different from having the value undefined. As you've already noticed, elided elements do contribute to the length of the array:
...the missing array element contributes to the length of the Array and increases the index of subsequent elements.
When you invoke indexOf on an array this is one of the steps that happens:
Let kPresent be the result of calling the [[HasProperty]] internal method of O with argument ToString(k).
In that, k is a number corresponding to an array index and O is the array itself. Since elided elements were not defined the array does not have a property for the corresponding index.
The .indexOf() function only examines elements of the array that have explicitly been set. Therefore, in this case, even though the length of the array is 1 (or 2, depending on the browser involved), there are no explicitly-set elements, so the effective length is zero.
Another way of seeing this effect:
var a = [,];
console.log(a.length); // 1 (in Firefox)
console.log('0' in a); // false
That means that even though the length of the array is 1, there is no element with index 0. Thus, any explicit reference to examine the value of a[0] will have the value undefined.
Now, if we play a little more:
a[0] = undefined;
console.log(a.length); // still 1
console.log('0' in a); // true !!
Once the property reference has appeared on the left side of an assignment, it becomes "real" even if its (now explicit) value is undefined.
As for the "internal methods" involved, you can check the Reference Specification type, and in particular how its "Get" operation works.
This is a tricky one. For instance, if you set a = [,,1] and examine the array, you'll see that only the index of 2 has a value, and it is 1. Index 0 & 1 have no value at all, they were implicitly set to undefined. In other words, they are NOT DEFINED. If you search for a value of undefined in an array, it will always return -1. If you instead set them explicitly to null, you'll see your index come back.
To directly address your question, a.indexOf(a[0]) returns -1 because a[0] is undefined.
All elements of an array which are not defined as a value (including null) are undefined.
When you define an array like this:
var a = [,null,];
Elements a[0] and a[2] are undefined. Any index you use into the array a will return undefined except a[1] which is null. The length property of an array is one higher than the highest non-undefined index. For example:
var b = [];
b[10] = null;
b.length
> 11
However the only index which will not return undefined is b[10].
You can read more about it here:
http://msdn.microsoft.com/en-us/library/d8ez24f2(v=vs.94).aspx
I am not sure what you mean by uninitialized - I think that concept is captured by undefined in JavaScript.
create 3 undefined, empty array.
var a1 = [,,,];
var a2 = new Array(3);
from JavaScript: The Definitive Guide,
0 in a1 //true
0 in a2 //false
but, in real world browser, getting different result. (IE8 and chrome 33...)
0 in a1 //false
0 in a2 //false
which is true, book or real world?
Looks like the book is wrong. As you can see from the specification, [,,,] does not add any values to the array:
The production ArrayLiteral : [ Elisionopt ] is evaluated as follows:
Let array be the result of creating a new object as if by the expression new Array() where Array is the standard built-in constructor with that name.
Let pad be the result of evaluating Elision; if not present, use the numeric value zero.
Call the [[Put]] internal method of array with arguments "length", pad, and false.
Return array.
("Elisions" are the , which are not preceded or followed by an expression.)
In simpler terms:
Create an empty array
Evaluate the ,, which is basically just counting them, starting from 1.
So ,,, results in 3.
Then set the length of the array to the result (e.g. 3).
And that's exactly what new Array(3) is doing as well.
There is also a less formal description of elided elements:
Array elements may be elided at the beginning, middle or end of the element list. Whenever a comma in the element list is not preceded by an AssignmentExpression (i.e., a comma at the beginning or after another comma), the missing array element contributes to the length of the Array and increases the index of subsequent elements. Elided array elements are not defined. If an element is elided at the end of an array, that element does not contribute to the length of the Array.
The Array constructor (new Array( 3 )) does set the length attribute to 3 but does not create any members of the array – not even undefined values.
In cases when there is only one argument passed to the Array constructor and when that argument is a Number, the constructor will return a new sparse array with the length property set to the value of the argument. It should be noted that only the length property of the new array will be set this way; the actual indexes of the array will not be initialized.
Source: http://bonsaiden.github.io/JavaScript-Garden/#array.constructor
Testing that in Chromium does indeed result in two false values…
The in operator returns false if the given key does not exist:
var a1 = [1,,3,4];
0 in a1 // true
1 in a1 // false
In the given code, best.test(password) is returning true but when I am using it in if()
condition in takes it as a false.
Code:
if(best.test(password)) //It takes it as a false .
{
document.write(best.test(password));
tdPwdStrength.innerHTML="best"+best.test(password); //but in actual it is true and returning true.
}
Please Suggest!
What is best? Is it a ‘global’ RegExp, that is, one with the g flag set?
If so, then each time you call test or exec you will get a different answer, as it remembers the previous string index and searches from there:
var r= /a/g; // or new RegExp('a', 'g')
alert(r.test('aardvark')); // true. matches first `a`
alert(r.test('aardvark')); // true. matches second `a`
alert(r.test('aardvark')); // true. matches third `a`
alert(r.test('aardvark')); // false! no more matches found
alert(r.test('aardvark')); // true. back to the first `a` again
JavaScript's RegExp interface is full of confusing little traps like this. Be careful.