Iterating through javascript HashTable without causing optimization loss - javascript

According to the 5.2.1 section of this article: Optimization killers
Doing this turns optimizations off in V8:
function hashTableIteration() {
var hashTable = {"-": 3};
for(var key in hashTable);
}
and the author says:
An object will go into hash table mode for example when you add too many properties dynamically (outside constructor), delete properties, use properties that cannot be valid identifiers and so on. In other words, when you use an object as if it was a hash
table, it will be turned into a hash table. Passing such an object to for-in is a no no. You can tell if an object is in hash table mode by calling console.log(%HasFastProperties(obj)) when the flag --allow-natives-syntax is enabled in Node.JS.
My question is then what is the correct way of iterating through the keys of a hashtable-like object in javascript, so that optimization do not get turned off?

Looks like the answer lies at the bottom of the very same article.
Workaround: Always use Object.keys and iterate over the array with
for loop. If you truly need all properties from entire prototype
chain, make an isolated helper function:
function inheritedKeys(obj) {
var ret = [];
for(var key in obj) {
ret.push(key);
}
return ret;
}
If you pass a object to for-in that is not a simple enumerable it
will punish the entire containing function.
From what I understood, the isolated function would help in allowing the rest of the process to be optimized. Only the inheritedkeys function wouldn't be optimized in the example below.
function someFunction(someObj) {
var keys = inheritedKeys(someObj),
i = 0,
len = keys.length;
for (; i < len; i++) {
//some computation
}
}

I believe Object.keys is regularly a better alternative in performance. However, I cannot say whether the optimizer converts it to a hashtable if used.
Update: Adding code for others stumbling upon this question.
var keys = Object.keys(myObj);
for (var i = 0; i < keys.length; i++) {
var value = myObj[keys[i]];
}

Related

OK to append a property to a javascript array? [duplicate]

Arrays have a "length" property by default.
Can I add custom properties to them?
Without having to make them objects.
var arr = [1,2,3,4,5];
arr.prop = 'value';
Arrays are already objects in JavaScript -- they just have some extra features and special literal syntax.
As other answers state, it's perfectly possible, because arrays in JavaScript are just objects. However, there is still the a question of whether it's a good idea or not.
That's a "coding style" question, so it's hard to say objectively, but Douglas Crockford doesn't have a problem with it (at least in some cases). In JavaScript: The Good Parts, he actually uses the example of adding a "total" method to an array.
Because an array is really an object, we can add methods directly to an individual array:
// Give the data array a total function
data.total = function () {
return this.reduce(add, 0);
};
total = data.total(); // total is 108
Since the string 'total' is not an integer, adding a total property to an array does not change its length.
(p62, Crockford's "JavaScript The Good Parts", found on Google Books)
However, it is worth mentioning that these extra properties are not included in JSON serialisation, and would be thrown away if you do anything like arr = arr.slice(1);.
If you meant to hide it in an Array, etc. (at least that's what I was looking for):
Object.defineProperty( targ, "myVal", { enumerable: false, writable: true } );
Then the array doesn't add it to length, so it's a property, not a value I guess you could say.
Then if you want to see the keys in an object for instance, Object.keys won't see it, but Reflect.ownKeys() will.
The Reflect version of defineProperty will return success or fail, while Object's returns the object passed.
Remember Reflect.preventExtensions() will (like it says) stop you or others from extending them.
Arrays are objects and therefore you can add your own properties to them:
var arr = [1, 2, 3];
arr.foo = 'bar';
Extending from other answers, here are the following ways of iterating over the array, excluding custom properties.
Using a standard for loop
for (let i = 0; i < arr_length; i++)
console.log(arr[i]);
or if not using ES6:
for (var i = 0; i < arr.length; i++)
console.log(arr[i]);
Using the for ... of loop (ES6+)
for (let el of arr)
console.log(el)
Using Array.prototype.forEach (ES6+)
arr.forEach(el => console.log(el));
or
arr.forEach(function(el) {
console.log(el);
});
Iterating over the array, including all properties (e.g., custom properties, length):
for (let prop in arr)
console.log(prop);
Or if not using ES6:
for (var prop in arr)
console.log(prop);
Yes. You can add them to the object by just declaring them and you can also extend Array using Array.prototype
var j = new Array();
j.yourprop = "foo";

Ordered Map Implementation in JavaScript

My Task
In my JavaScript code i'm often using objects to "map" keys to values so i can later access them directly through a certain value. For example:
var helloMap = {};
helloMap.de = "Hallo";
helloMap["en"] = "Hello";
helloMap.es = "Hola";
So i build up the map object step by step in my source code using the two available notations object style and array style.
Later i can then access the values i added through helloMap["de"] for example. So thats all fine if i don't have to care about the order in which the attributes has been set on the object.
If i want to iterate the objects properties now as far as i know there is no way to ensure that i'll iterate them in the order they have been added (insertion order).
Note: I can't use some wrapper object and simply hold a array in there and then use its methods to add the values so something like this:
var HelloMap = function(){
this.myMap = [];
this.addProperty = function(key, value){
this.myMap.push({key: key, value: value});
}
}
or something similar won't work for me. So the solution needs to be absolutely transparent to the programmer using the object.
That said the object i needed would be an empty object which maintains the order of the properties that were added to it. Something like this would do:
var helloMap = {};
helloMap = getOrderAwareObject(helloMap);
so that every further assignment of the form helloMap.xy = "foo" and helloMap["yz"] = "bar" would be tracked in the object "in order",
Possible Solutions
Since i did not find any solution in underscore or jQuery giving me such a special object i came across the possibility of defining getters and setters for properties in JavaScript objects with Object.defineProperty since i can rely on ECMAScript 5 standard i can use it.
The Problem with this one is, that you have to know all the possible properties that can be set on the object, before they are actually set. Since if you define it you got to name it.
What i am searching for is something like a Default Getter and Default Setter which applies on the object if no getter and setter has been defined for the property. So i could then hide the sorted map behind the object inteface.
Is there already a solution for this in any framework you know?
Is there a mechanism like "default getter/setter" ?
You'll need a wrapper of some kind using an array internally, I'm afraid. ECMAScript 5 (which is the standard on which current browser JavaScript implementations are based) simply doesn't allow for ordered object properties.
However, ECMAScript 6 will have a Map implementation that has ordered properties. See also http://www.nczonline.net/blog/2012/10/09/ecmascript-6-collections-part-2-maps/.
There may also be other options in ECMAScript 6. See the following question:
How can I define a default getter and setter using ECMAScript 5?
Adding a link to a custom javascript library which provides Sorted maps and other implementation, for future reference in this thread . Check out https://github.com/monmohan/dsjslib
-msingh
I don't know of a general solution but non-general solutions are very simple to construct.
Typically, you maintain an Array of objects, with several methods defined as properties of the Array. At least, that's my approach.
Here's an example, taken (in a modified form) from a larger application :
var srcs = [];
srcs.find = function(dist) {
var i;
for(i=0; i<this.length; i++) {
if(dist <= this[i].dist) { return this[i]; }
}
return null;
};
srcs.add = function(dist, src) {
this.push({ dist:dist, src:src });
}
srcs.remove = function(dist) {
var i;
for(i=0; i<this.length; i++) {
if(this[i].dist === dist) {
srcs.splice(i,1);
return true;
}
}
return false;
};
srcs.add(-1, 'item_0.gif' );
srcs.add(1.7, 'item_1.gif');
srcs.add(5, 'item_2.gif');
srcs.add(15, 'item_3.gif');
srcs.add(90, 'item_4.gif');
Unfortunately, you lose the simplicity of a plain js object lookup, but that's the price you pay for having an ordered entity.
If you absolutely must have order and dot.notation, then maintain a plain js Object for lookup and an Array for order. With care, the two can be maintained with total integrity.
See my answer to this question. I implemented an basic ordered hashtable (ES 5+ only, didn't bother to polyfill)
var put = function(k,v){
if(map[k]){
console.log("Key "+ k+" is already present");
}else
{
var newMap = {};
map[k] = v;
Object.keys(map).sort().forEach(function(key){
newMap[key] = map[key];
});
map = newMap;
//delete newMap; in case object memory need to release
return map;
}
}
Put method will always take a key-value pair, internally creates another map with sorted keys from the actual map, update the value and return the updated map with sorted keys.No external library need to includ.

Monkeypatched code appearing in for...in loop over Array

I have the following code in my web app's main JavaScript:
// uniq for arrays
if (!Array.prototype.getUnique) {
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;
}
}
It's a simple uniq clone for javascript, monkeypatched into the basic Array class. (Not here to debate monkeypatching, flame elsewhere please...)
getUnique() works as intended, but now whenever I iterate over an Array with a for...in loop, an additional index called getUnique is passed to the iterator body, and when this false n is looked up, getUnique's code is the value. (In other words: for...in is looping over the array as it should, but appending a getUnique as the last iteration)
What is going on here? There's another function monkeypatched in right above this one that works fine (indexOf()) and does not appear in the iterator. Here is some example code that triggers this issue:
for (var r in result) {
//tags = tags + result[r]["tags"].split(" ").join(", ")
if (result[r]["tags"]) {
var newtags = result[r]["tags"].split(" ");
debug.log(newtags);
for (var n in newtags) {
debug.log("n is " + n);
tags.push(newtags[n]);
}
}
}
The debug output from this snippet looks like:
[14:22:26.090] [["camp", "furnitur", "wood"]]
[14:22:26.093] ["n is 0"]
[14:22:26.096] ["n is 1"]
[14:22:26.099] ["n is 2"]
[14:22:26.101] ["n is getUnique"]
What is going on here? Refactoring the monkeypatch and dropping it in a utility class is easy enough, but this is a really strange edge case to me. I have some ideas in my head about what is happening here, but those are just guesses. Can anyone explain?
The common misconception is that the for...in operation is, in javascript, made to iterate Arrays. This is wrong. It's usage is to iterate over any enumerable property of an object. In javascript, everything is an object (Arrays included). But you may notice, when you do for...in on an array, you don't get values out like length, slice, etc. This is because those are NOT enumerable properties of the object.
This SO question has some really good information on the usage of for...in: Why is using "for...in" with array iteration a bad idea?
If you want to stick to using for...in, you can do like was suggested above and check for hasOwnProperty inside your for...in
for ( var v in myArray ) {
if ( myArray.hasOwnProperty(v) ) {
// Do Something
}
}
I, however, suggest using plain old boring loops...
for ( var i = 0; i <= myArray.length - 1; i++ ) {
// Do Something
}
Without going into crazy detail, this works without hitting your added methods because the "indexes" of the array are not really indexes at all, but are instead properties with a name that matches their corresponding index: i.e 1, 0, 4, etc.
They feel like indexes because in javascript you can't access a property with dot notation if that property is a number (i.e: myArray.0 will not work). So you do myArray[0], and that feels like an array.
This is what hasOwnProperty is designed to solve: it tells you whether the name you've iterated to is on the object itself, or is inherited from the prototype.
Try this:
for (var n in newtags) {
if (newtags.hasOwnProperty(n)) {
debug.log("n is " + n);
tags.push(newtags[n]);
}
}
You shouldn't iterate over an array using for…in loop, that is intended for enumerating properties of objects. So, for instance, you have no guarantee about the order of the indexes, neither – as you saw – that you're iterate only numeric indexes. If someone else adds a property to the Array's prototype in such way, you will iterate that property as well. See https://developer.mozilla.org/en/JavaScript/Reference/Statements/for...in to have the complete picture.
However, in your case, you can do the follow:
for (var n in newtags) {
if (newtags.hasOwnProperty(n)) {
debug.log("n is " + n);
tags.push(newtags[n]);
}
}
That also prevent to iterate properties that are added by other scripts to the prototype. Also, you could define your function as not enumerable (in the browser where ES5 is supported):
Object.defineProperty(Array.prototype, "getUnique", {
value: function () { /*.. your function ..*/}
})
See : https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/defineProperty
Don't use for-in for iteration of numeric properties.
Use a for loop:
for (var i = 0; i < newtags.length; i++) {
The for-in statement is almost never the right tool for this job, because...
It includes all properties, including non-numeric, out of range, and prototyped properties.
It makes no guarantee of the order of enumeration.
While you could do a hasOwnProperty check, there's no real benefit, and has some down-sides...
It slows down your iteration
It doesn't help with the order of enumeration issue
Sometimes you want a prototyped index (rare, but it happens). Using hasOwnProperty makes this impossible.

Add a property to a JavaScript array

Arrays have a "length" property by default.
Can I add custom properties to them?
Without having to make them objects.
var arr = [1,2,3,4,5];
arr.prop = 'value';
Arrays are already objects in JavaScript -- they just have some extra features and special literal syntax.
As other answers state, it's perfectly possible, because arrays in JavaScript are just objects. However, there is still the a question of whether it's a good idea or not.
That's a "coding style" question, so it's hard to say objectively, but Douglas Crockford doesn't have a problem with it (at least in some cases). In JavaScript: The Good Parts, he actually uses the example of adding a "total" method to an array.
Because an array is really an object, we can add methods directly to an individual array:
// Give the data array a total function
data.total = function () {
return this.reduce(add, 0);
};
total = data.total(); // total is 108
Since the string 'total' is not an integer, adding a total property to an array does not change its length.
(p62, Crockford's "JavaScript The Good Parts", found on Google Books)
However, it is worth mentioning that these extra properties are not included in JSON serialisation, and would be thrown away if you do anything like arr = arr.slice(1);.
If you meant to hide it in an Array, etc. (at least that's what I was looking for):
Object.defineProperty( targ, "myVal", { enumerable: false, writable: true } );
Then the array doesn't add it to length, so it's a property, not a value I guess you could say.
Then if you want to see the keys in an object for instance, Object.keys won't see it, but Reflect.ownKeys() will.
The Reflect version of defineProperty will return success or fail, while Object's returns the object passed.
Remember Reflect.preventExtensions() will (like it says) stop you or others from extending them.
Arrays are objects and therefore you can add your own properties to them:
var arr = [1, 2, 3];
arr.foo = 'bar';
Extending from other answers, here are the following ways of iterating over the array, excluding custom properties.
Using a standard for loop
for (let i = 0; i < arr_length; i++)
console.log(arr[i]);
or if not using ES6:
for (var i = 0; i < arr.length; i++)
console.log(arr[i]);
Using the for ... of loop (ES6+)
for (let el of arr)
console.log(el)
Using Array.prototype.forEach (ES6+)
arr.forEach(el => console.log(el));
or
arr.forEach(function(el) {
console.log(el);
});
Iterating over the array, including all properties (e.g., custom properties, length):
for (let prop in arr)
console.log(prop);
Or if not using ES6:
for (var prop in arr)
console.log(prop);
Yes. You can add them to the object by just declaring them and you can also extend Array using Array.prototype
var j = new Array();
j.yourprop = "foo";

Wrapping For in loops with if statements in Javascript -- looping over arrays

JSLint keeps complaining about things like this
var myArray = [1, 2, 3];
for (var value in myArray)
{
// BLAH
}
Saying that I should wrap it in an if statement. I realize you need to wrap it if you are looping over an object's properties, but here what should I put in the if statement to do the correct filtering.
Additionally when I do something like
for (var i = 0; i < 10; i++)
{
// foo
}
for (var i =0; i < 20; i++)
{
// bar
}
It complains that i has already been defined. How do I prevent this other than using different variable names?
JSLint whinges about a lot that's not really harmful. In this case it's right to complain about for...in, because that's the wrong construct to loop over an Array.
This is because you will get not only the numeric keys, but also any other arbitrary properties that have been added to the array or its Array.prototype. The latter typically comes from extension utility functions added by frameworks.
Whilst you can defeat that case with hasOwnProperty to check it's not a prototype member, it's uglier than just doing it the proper way with for (var i= 0...) so why bother.
Also, with for...in you won't necessarily get the items in numerical order as you might expect.
It complains that i has already been defined. How do I prevent this other than using different variable names?
Yeah, you can ignore that one.
It wants you to remove the var from the second for (i..., because declaring a variable twice in the same scope doesn't do anything. However I would recommend leaving the var there because it doesn't do any harm, and if you move the loop to another block you don't want it to be suddenly scribbling on globals.
Really, you don't have to listen to jslint. But if you really want to just pass (which is nice) you might do:
var myArray = [1, 2, 3];
for (var value in myArray)
{
if (myArray.hasOwnProperty(value)) {
// BLAH
}
}
For the second part, you either have to put them in functions or use different variables. The other solution would be to just use i instead of var i the second time, because it's already defined...
If you look at the JSLint docs you'll find a link explaining the rationale behind filtering for-in loops: basically, it's to avoid tripping over any enumerable properties that have been added to the object's prototype. (Although you shouldn't use for-in to iterate over an array anyway.)
In the second case you are declaring the variable twice: variables have function scope (or global scope) in JavaScript. Douglas Crockford, and therefore JSLint, argues that it is better to declare the variable only once for the scope in which it resides:
var i;
for (i = 0; i < 10; i++)
{
// foo
}
for (i =0; i < 20; i++)
{
// bar
}
I suggest to follow JSLint as good reference point, you may want to configure few options and make you check looser.
Anyway the best way to iterate through an Array is using for loop rather than for in loop.
If you want a detailed explanation read this post

Categories