javascript: Prevent representing added Array's attributes as elements (in Chrome) - javascript

I am using this snippet to extend Array's functionality with getUnique function:
Array.prototype.getUnique = function() {
var u = {}, a = [];
for (var i = 0, l = this.length; i < l; ++i) {
if (u.hasOwnProperty(this[i])) {
continue;
}
a.push(this[i]);
u[this[i]] = 1;
}
return a;
};
The slight drawback is that the attribute getUnique is printed together with array elements if I console.log an array:
["126", "125", getUnique: function]
It is slightly disturbing even if I know that getUnique is not considered as an array element. This happens in Chrome. Not in Firefox as far as I know. Is there a way to prevent it?

Use Object.defineProperty and enumerable:
Object.defineProperty(Array.prototype, 'getUnique',
{ enumerable: false
, configurable: true
, writable: true
, value: function ()
{ var u = {};
var a = [];
for (var i = 0, l = this.length; i < l; ++i)
{ if (u.hasOwnProperty(this[i])) continue;
a.push(this[i]);
u[this[i]] = 1;
}
return a;
}
});
By default, a property is enumerable (even if it's not a direct property of an instance), which means that it is visible through any for-in loop (or Object.keys) of the array. Chrome does something similar to a for-in loop on an array, which is why it is represented as it is.
Note that this function only works in (relatively) modernish browsers which support ES5, which means that IE < 9, some older Android browsers and other mobile devices may not be able to use this code. This is one of the reasons why some people recommend against extending the prototypes of native constructors, as this issue can break code that isn't well constructed against these issues. What people who believe this usually recommend is to not place the function on the prototype:
function getUnique(arr)
{ var u = {};
var a = [];
for (var i = 0, l = array.length; i < l; ++i)
{ if (u.hasOwnProperty(arr[i])) continue;
a.push(arr[i]);
u[arr[i]] = 1;
}
return a;
}
I'd also like to point out that only one object will be ever printed by the getUnique function, as it is technically incorrect in its implementation. While rather similar, objects are not dictionaries and can only have string keys, which means that any object (no matter the contents) will have a string value of '[object Object]', which means that when your function encounters another object (even if it has different values), it will not be added to the returned array. While inefficient, the only way I can think of is to iterate through the array each time:
function getUnique(arg)
{ 'use strict';
var O = Object(arg);
var len = O.length >>> 0;
var A = [];
var indexOf = function (arg, searchElement)
{ for (var i = 0, l = arg.length; i < l; ++i) if (arg[i] === searchElement) return i;
return -1;
};
for (var k = 0; k < len; ++k)
{ var elementK = O[k];
var kPresent = k in O;
if (!kPresent || indexOf(A, elementK) !== -1) continue;
A[A.length - 1] = elementK;
}
return array;
}

Related

Huge performance difference between methods of calculating a hash. This can't be right, right? [duplicate]

I wanted to extend String object prototype with some utility method. It worked, but the performance was surprisingly low. Passing a string to a function is 10x times faster than overriding the String.prototype method that is doing the same thing. To make sure this really happens I created a very simple count() function and the corresponding methods.
(I was experimenting, and created three different versions of the method.)
function count(str, char) {
var n = 0;
for (var i = 0; i < str.length; i++) if (str[i] == char) n++;
return n;
}
String.prototype.count = function (char) {
var n = 0;
for (var i = 0; i < this.length; i++) if (this[i] == char) n++;
return n;
}
String.prototype.count_reuse = function (char) {
return count(this, char)
}
String.prototype.count_var = function (char) {
var str = this;
var n = 0;
for (var i = 0; i < str.length; i++) if (str[i] == char) n++;
return n;
}
// Here is how I measued speed, using Node.js 6.1.0
var STR ='0110101110010110100111010011101010101111110001010110010101011101101010101010111111000';
var REP = 1e3//6;
console.time('func')
for (var i = 0; i < REP; i++) count(STR,'1')
console.timeEnd('func')
console.time('proto')
for (var i = 0; i < REP; i++) STR.count('1')
console.timeEnd('proto')
console.time('proto-reuse')
for (var i = 0; i < REP; i++) STR.count_reuse('1')
console.timeEnd('proto-reuse')
console.time('proto-var')
for (var i = 0; i < REP; i++) STR.count_var('1')
console.timeEnd('proto-var')
Results:
func: 705 ms
proto: 10011 ms
proto-reuse: 10366 ms
proto-var: 9703 ms
As you can see the difference is dramatic.
The below proves that performance of method calls is neglectably slower, and that the function code it self is slower for methods.
function count_dummy(str, char) {
return 1234;
}
String.prototype.count_dummy = function (char) {
return 1234; // Just to prove that accessing the method is not the bottle-neck.
}
console.time('func-dummy')
for (var i = 0; i < REP; i++) count_dummy(STR,'1')
console.timeEnd('func-dummy')
console.time('proto-dummy')
for (var i = 0; i < REP; i++) STR.count_dummy('1')
console.timeEnd('proto-dummy')
console.time('func-dummy')
for (var i = 0; i < REP; i++) count_dummy(STR,'1')
console.timeEnd('func-dummy')
Results:
func-dummy: 0.165ms
proto-dummy: 0.247ms
Although on huge repetitions (like 1e8) prototyped methods proves to be 10x times slower than functions, this can be ignored for this case.
All this may be related only to a String object, because simple generic objects perform about the same when you pass them to functions or call their methods:
var A = { count: 1234 };
function getCount(obj) { return obj.count }
A.getCount = function() { return this.count }
console.time('func')
for (var i = 0; i < 1e9; i++) getCount(A)
console.timeEnd('func')
console.time('method')
for (var i = 0; i < 1e9; i++) A.getCount()
console.timeEnd('method')
Results:
func: 1689.942ms
method: 1674.639ms
I've been searching on Stackoverflow and binging, but other that the recommendation "do not extend String or Array because you pollute the name space" (which is not a problem for my particular project), I cannot find anything related to performance of methods compared to functions. So should I simply forget about extending the String object due to performance drop of added methods or there is more about it?
This is most likely because you are not using strict mode, and the this value inside your method is getting coerced to a String instance instead of being a primitive string. This coercion, and further method calls or property accesses on the String object, are slower than using primitive values.
You can (Edit: could, at least, in 2016) confirm this by repeating your measurement on var STR = new String('01101011…') which should have less overhead.
Then fix your implementation:
String.prototype.count = function (char) {
"use strict";
// ^^^^^^^^^^^^
var n = 0;
for (var i = 0; i < this.length; i++)
if (this[i] == char)
n++;
return n;
};

Polyfill for push method in JavaScript

In recent interview, interviewer has asked can you write polyfill for push() method in javascript.
any one know how to do this .?
push() adds one or more elements at the end of array and returns new length of array. You can use array's length property to add element at the end of it.
if (!Array.prototype.push) {
// Check if not already supported, then only add. No need to check this when you want to Override the method
// Add method to prototype of array, so that can be directly called on array
Array.prototype.push = function() {
// Use loop for multiple/any no. of elements
for (var i = 0; i < arguments.length; i++) {
this[this.length] = arguments[i];
}
// Return new length of the array
return this.length;
};
}
if (!Array.prototype.push) {
Array.prototype.push = function () {
for (var i = 0, len = arguments.length; i < len; i++) {
this[this.length] = arguments[i];
if (Object.prototype.toString.call(this).slice(8, -1).toLowerCase() === 'object') {
this.length += 1;
}
}
return this.length;
};
}

Array.prototype.indexOf being added to end of arrays in IE8

I know IE8 and earlier doesn't have an indexOf function. I'm defining it as follows:
if (!Array.prototype.indexOf) {
Array.prototype.indexOf = function(obj, start) {
for (var i = (start || 0), j = this.length; i < j; i++) {
if (this[i] === obj) { return i; }
}
return -1;
}
}
I can correctly get the index of values in an array, but the function is being added to the end of my arrays when using IE8 and earlier. Therefore, I'm getting things like:
obj.obj2[0] = 'data'
obj.obj2[1] = 'other data'
obj.obj2['indexOf'] = [definition of indexOf function]
Not surprisingly, this is breaking everything else on the site. Problem isn't happening in IE10 or 9. Any and all help is appreciated.
It is added to the prototype, so everytime you treat your array like an object (for..in loop is one example), it will show up. It does not show up in other browsers because they already have the indexOf method by default, so you're not modifying the prototype.
You can use obj.hasOwnProperty(propertyName) to test whether a property is defined directly on your object (in this case, your array, which is an object basically) or somewhere else in the prototype chain.
Sounds like you might be doing something wrong when iterating through your arrays. I ran the following in IE8 and didn't get the behavior you mentioned:
if (!Array.prototype.indexOf) {
Array.prototype.indexOf = function(obj, start) {
for (var i = (start || 0), j = this.length; i < j; i++) {
if (this[i] === obj) { return i + " used prototype"; }
}
return -1;
}
}
var myCars=new Array("Saab","Volvo","BMW");
document.write(myCars.indexOf("Volvo") + "<br/><br/>");
for (i = 0; i < myCars.length; i++)
document.write(i + ": " + myCars[i] + "<br/>");
output:
1 used prototype
0: Saab
1: Volvo
2: BMW
In the case when we use Array.prototype.someFunction,
to filter truly array's elements in IE 8, we can use:
for (var i = 0, len = myArray.length; i < len; i++) {
if (typeof myArray[i] === "function") {break;}
// some code for myArray trully elements
}
IE<9 doesn't have an .indexOf() function for Array, to define the exact spec version, run this before trying to use it:
if (!Array.prototype.indexOf)
{
Array.prototype.indexOf = function(elt /*, from*/)
{
var len = this.length >>> 0;
var from = Number(arguments[1]) || 0;
from = (from < 0)
? Math.ceil(from)
: Math.floor(from);
if (from < 0)
from += len;
for (; from < len; from++)
{
if (from in this &&
this[from] === elt)
return from;
}
return -1;
};
}
Reference

Iterating Performance Comparison

I have an array:
deleteIds= ["User1"];
and try to iterate over it as like:
first one:
for (var index = 0; index < len; index++) {
alert(deleteIds[index]);
}
second one:
for (var index in deleteIds) {
alert(deleteIds[index]);
}
What is the performance comparison of them?
This is a micro optimisation at best. Unless you have hundreds of thousands of elements in your deleteIds array, you should not be looking at optimising this.
It turns out that the for(;;;) version is quicker than for in: http://jsperf.com/iterating-over-a-loop/2 (thanks to #Jamiec for the jsperf).
However, what's more important than the performance comparison of these, is that both code snippets are functionally different to each other (live example).
var array = new Array(100);
for (var i = 0; i < array.length; i++) {
console.log(i); // shows 0 - 99
}
for (var x in array) {
console.log(x); // shows nothing
}
Furthermore, if you add methods to either the array, or to an object in the arrays' prototype chain, they will show up in the for (var x in y) loop, but not in for (;;;); (live example):
Array.prototype.foo = function() {};
var array = [];
for (var i = 0; i < array.length; i++) {
console.log(i); // nothing
}
for (var x in array) {
console.log(x); // `foo`
}
You can use hasOwnProperty to eliminate the attributes inherited from the prototype chain, but that won't stop you receiving methods directly on the object (live example):
Array.prototype.foo = function() {};
var array = [];
array.bar = function () {};
for (var i = 0; i < array.length; i++) {
console.log(i); // nothing
}
for (var x in array) {
if (array.hasOwnProperty(x)) {
console.log(x); // `bar`
}
}
It is because of these reasons that using for in for iterating over an array is discouraged, and the for(;;;) version should always be used instead. for in should be used for iterating over an object.

Javascript: Filtering twodimensional array

There's plenty examples available on how to sort an javascript array based on it's numeric values. What would however be appropriate way to fetch all elements from myArray with the property prop1 with it's according value value1?
Here's my array:
var myArray = [
{
"id":"2",
"name":"My name",
"properties":{"prop1":"value1"}
}];
Thanks
You can just access it by dot or bracket notation and push the matching members to your new/filtered array, for example:
var newArray = [];
for(var i=0, l = myArray.length; i<l; i++) {
if(myArray[i].properties.prop1 == "value1") newArray.push(myArray[i]);
}
Your question is a bit ambiguous though, if you're trying to get the {"prop1":"value1"} object, not the parent, then just change newArray.push(myArray[i]) to newArray.push(myArray[i].properties).
Provide a compare function to sort by arbitrary properties:
function compareMyObjects(a, b) {
var valA = a.properties.prop1.value1;
var valB = b.properties.prop1.value1;
if(valA > valB) return 1;
if(valA < valB) return -1;
return 0;
}
myArray.sort(compareMyObjects);
https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/sort
Go through each element in your array. For each element, check each property to see if it matches the one you're looking for.
function filterArray(array, property, value) {
var newArray = [];
for (var i = 0; i < array.length; i++) {
for (var j in array[i].properties) {
if (j === property && array[i].properties.hasOwnProperty(j)) {
if (array[i].properties[j] == value) {
newArray.push(array[i]);
}
}
}
}
}
var newarray=myarray.filter(function(itm){
return itm.properties.prop1==='value1';
});
Filter, like the array methods indexOf and map, may be worth providing for browsers that don't have it- this version is from Mozilla Developer Site-
if(!Array.prototype.filter){
Array.prototype.filter= function(fun, scope){
var L= this.length, A= [], i= 0, val;
if(typeof fun== 'function'){
while(i< L){
if(i in this){
val= this[i];
if(fun.call(scope, val, i, this)){
A[A.length]= val;
}
}
++i;
}
}
return A;
}
}

Categories

Resources