Is this usage of for .. in acceptable? - javascript

I'd like to do the same thing using several strings. I'd like to do it like this:
names = ["NOTE", "REPLICA", "HINT", "VIEW"];
for (name in names) {
name = names[name];
//do stuff
}
Then I read this. Is it still OK?

It's better to go through an array using a number:
var i = 0;
for(i=0;i<names.length;i++){
...
}
the article you link to already mentioned that any other object properties including stuff on Array.prototype or Object.prototype will show up in for ... in another reason not to use it is that for .. in for Array's is slower.
That article does mention an edge case where for ... in could be faster when the lenght of the array is big but only a couple of items are set. In that case I guess you can use for ... in with hasOwnProperty and checking if the property is a number:
var stuff, index;
stuff = [];
stuff[0] = "zero";
stuff[9999] = "nine thousand nine hundred and ninety-nine";
stuff.name = "foo";
for (index in stuff)
{
if (stuff.hasOwnProperty(index) && String(Number(index)) === index) {
console.log("stuff[" + index + "] = " + stuff[index]);
}
}

It's a lot slower than just using a for loop.
Around 86% slower in my browser (Google Chrome 28.0.1500.72).
Check out this benchmark I made.
While for..in loops only ran at 110,772 ops/sec (still fast), for loops ran at 791,792 ops/sec
I typically use for..in loops with objects. I believe that's what they are actually for.

Related

Call join() on a generator in JS

In Python, you can call the string.join() method on any iterable, like so:
",".join(some_iterable)
The argument may be a list, generator, or any other object as long as it is iterable.
Playing around with ES6, I couldn't find a way to do so without having to create an array first, I had to do something like this:
function *myGenerator() { ... }
let output = [...myGenerator()].join(",");
I know that join() is an Array.prototype method. Is it possible for me to call join() or some equivalent to concatenate the values generated by myGenerator without having to create an intermediate array, like the python example above?
The comments above answer your question pretty well. However, I was curious about #jonrsharpe's comment about generating intermediate strings and was wondering how that actually affected performance. So I put a join method on the prototype of a generator and tested it.
Here's the join() code:
function* nGen(start, stop) {
while (start < stop) {
yield start
start++
}
}
nGen.prototype.join = function(sep) {
let res = this.next().value
for (let v = this.next(); !v.done; v = this.next()) {
res += sep + v.value
}
return res
}
let g = nGen(2, 20)
console.log(g.join(','))
The original jsPerf tests in Safari and Chrome showed this working faster than the very common idiom: [...g].join(','). In a JSBench test in 2022, results were inconsistent between Chrome, Firefox, and Edge, but this method is now slower than [...g].join(',') and/or a for loop.
I am far from a master of writing jsPerf/JSBench tests, so maybe I'm screwing something up or misinterpreting the result. But if you're interested: https://jsbench.me/zlkz8tm6vw/1

ES6 reverse iterate an array using for..of, have I missed something in the spec?

In ES6 we now have iterators and for..of to iterate them. we have some built-ins for arrays; notably keys, values and entries.
These methods allow one to perform much of the iteration one would commonly perform. But, what about iteration in reverse? This is also a very common task and I don't see anything in the spec specifically for it? Or maybe I missed it?
Ok, we have Array.prototype.reverse but I don't necessarily want to reverse a large array in place and then reverse it again when finished. I also don't want to use Array.prototype.slice to make a temporary shallow copy and reverse that just for iteration.
So I took a look a generators and came up with these working solutions.
(function() {
'use strict';
function* reverseKeys(arr) {
let key = arr.length - 1;
while (key >= 0) {
yield key;
key -= 1;
}
}
function* reverseValues(arr) {
for (let key of reverseKeys(arr)) {
yield arr[key];
}
}
function* reverseEntries(arr) {
for (let key of reverseKeys(arr)) {
yield [key, arr[key]];
}
}
var pre = document.getElementById('out');
function log(result) {
pre.appendChild(document.createTextNode(result + '\n'));
}
var a = ['a', 'b', 'c'];
for (var x of reverseKeys(a)) {
log(x);
}
log('');
for (var x of reverseValues(a)) {
log(x);
}
log('');
for (var x of reverseEntries(a)) {
log(x);
}
}());
<pre id="out"></pre>
Is this really the way that reverse iteration is intended in ES6 or have I missed something fundamental in the spec?
Is this really the way that reverse iteration is intended in ES6?
There was a proposal for reverse iteration, discussed on esdicuss and a git project outlining a spec, but nothing much seemed to happen with respect to it. ES6 is finalised now, so it's not something that is going to be added this time around. Anyway, for arrays and strings I've written a little code to fill in the gaps (in my opinion) and I will post it here as it may help others. This code is based on my browsers today and some improvements could possibly be made if there was more of ES6 implemented on them. I may get around to a gist or a small github project later.
Update: I have created a GitHub project for work on this.
Have a look at https://www.npmjs.com/package/itiriri.
It's a library that has similar methods as arrays, but works with iterators.
import { query } from 'itiriri';
const m = new Map();
m.set(1, 'a');
m.set(2, 'b');
m.set(3, 'c');
const result = query(m);
for (const [k, v] of result.reverse()) {
console.log(k + ' - ' + v)
}
query returns an iterable that has similar methods as arrays. In above example reverse() is used. There are also fitler, slice, map, concat etc.
If you need back an array, or a map from a query you can use one of .toArray(), .toMap() or .toSet() methods.

How efficient is the "with" statement?

It is hard to Google for some keywords like "with" word, so I am testing to ask here.
Is the with statement in JavaScript inefficient?
For instance, say I have:
with(obj3) {
with(obj2) {
with(obj1) {
with(obj0) {
eval("(function() { console.log(aproperty) })();");
}
}
}
}
Would the above be more or less efficient, if for instance, I walked over obj0, obj1, obj2, obj3 and merged them together, and then used either:
One with statement alone
Created a parameters string with the keys of obj0, obj1, obj2 and obj3, and an args array for the values and used:
eval("function fn(aproperty, bproperty) { console.log(aproperty); }")
fn.apply(undefined, args);
Which of these three approaches can be deemed to be quicker? I am guessing on with statements but so many with's makes me think I can optimize it further.
If you're looking for options, then you may want to consider a third approach, which would be to create (on the fly if needed) a prototype chain of objects.
EDIT: My solution was broken. It requres the non-standard __proto__ property. I'm updating to fix it, but be aware that this isn't supported in all environments.
var objs = [null,obj3,obj2,obj1,obj0];
for (var i = 1; i < objs.length; i++) {
objs[i].__proto__ = Object.create(objs[i-1]);
}
var result = objs.pop();
This avoids with and should be quicker than merging, though only testing will tell.
And then if all you needed was a product of certain properties, this will be very quick.
var props = ["x2","b1","a3"];
var product = result.y3;
for (var i = 0; i < props.length; i++)
product *= result[props[i]];
Newer browsers have an internal tokening mechanism to make the javascript interpretation cheaper. It is very like JIT in the newer JVMs. I think there isn't a much problem with your deeply embedded with-s, practically it will be some like
__get_aproperty() {
if (obj0.has("aproperty")) return obj0.aproperty;
if (obj1.has("aproperty")) return obj1.aproperty;
if (obj2.has("aproperty")) return obj2.aproperty;
if (obj3.has("aproperty")) return obj3.aproperty;
}
So, the structure of your js is highly embedded, but the structure of the real execution in the JS-engine of the browsers, will be simple and linear.
But the tokenization of the JS, that is costly. And if the JS-engine finds an eval, needs to tokenize.
I voted for the first version.
With statement will make your code run like it's 1980 - literally every optimization implemented in a JIT cannot be used when it's in effect.

Which is faster, For Loop or .hasOwnProperty?

I was working on a project where I needed to pull a list of excluded users out of a giant list of user data. It made me wonder if it is faster to use a double for loop with excluded id's in an array. Or if putting the id's in object properties and using .hasOwnProperty() is faster.
var mainList = LARGE JSON OBJECT OF DATA.
var eArray = ["123456","234567","345678","456789","012345"];
var eObject = {"123456":"0","234567":"0","345678":"0","456789":"0","012345":"0"};
Using the Double For Loop Approach:
for(i=0; i < mainList.length; i++){
for(j=0; j < eArray.length; j++){
if(mainList[i]['id'] === eArray[j]){
//Do Something
}
}
}
Using the .hasOwnProperty() Approach:
for(i=0; i < mainList.length; i++){
if(eObject.hasOwnProperty(mainList[i]['id'])){
//Do Something
}
}
I realize there are other ways to make the loops faster, like storing lengths in variables. I've tried to simplify this.
Thanks for any information.
You've missed out on a third, faster alternative. Provided you haven't been tinkering with the Object.prototype in any way, and the ID's are unlikely to be prototype values (like valueOf and the like), you could simply use a for loop like so:
for(var i=0; i < mainList.length; i++)
{
if (eObject[mainList[i].id] !== undefined)
{//or typeof eObject[mainList[i].id] !== 'undefined'
//do something
}
}
Check the updated JSPref, it's the fastest way by far (57,252,850 ops/sec vs 17,503,538 ops/sec for the double loop)
If you think about it, it would make sense that the .hasOwnProperty() approach would be faster because it uses only 1 for loop.
WRONG
I was actually a little surprised. I was expecting the double loop to be slower. But I guess you can't under estimate the speed of a for loop.
Double Loop
While this to me would seem like the slowest, this actually ended up being the fastest benching at 7,291,083 ops/sec
.hasOwnProperty()
I can see how this would be slower because functions are slower than statements. This benches at 1,730,588 ops/sec
if..in
#Geuis answer included the if..in statement and thought I would test the speed of that which would seem the fastest but benching at 2,715,091 ops/sec it still doesn't beat the for loops.
Conclusion
For loops are fast. The double loops runs more than 4 times faster than using .hasOwnProperty() and almost 3 times faster than using the if..in condition. However, the performance is not really noticeable; so is speed really that important that you have the need to complicate things. In my opinion the if..in method is the way to go.
Test this yourself in your browser. I was using Google Chrome 28.
Update
It is important to note that using an for..in declaration will give you the best performance.
Edit to show the proper code for future internet traversers: Visit http://jsperf.com/stackoverflow-for-vs-hasownproperty/5 for the comparison
var testVal = 'BigBrownFox',
arr = [1,4,'asd','BigBrownFox',9];
if( arr.indexOf('testVal') > -1 ){
//do something
}
For testing an array of values for existence in another array:
var testVal = ['BigBrownFox'],
arr = [1,4,'asd','BigBrownFox',9];
for(var i=0, len=testVal.length; i<len; i++){
if( arr.indexOf(testVal[i]) > -1 ){
//do something
}
}
Actually, your approach in both cases is slightly off.
If are using an array, just use the indexOf function. If the testing value exists, it will return its index. Otherwise it's -1 if not found. No loop is needed at all.
In the case of an object, you don't use .hasOwnProperty. Yeah, it does what you want but its overcomplicated and slower because you're doing a function call.
Just use
var eObject = {"123456":"0","234567":"0","345678":"0","456789":"0","012345":"0"};
if( '234567' in eObject ){ //do something }
Hope this helps.
in chrome the fastest loop is for
in older/other browsers the fastest one is the while-- loop.
especially if you cache the length.(very important if the mainList is big)
as i see you have only strings in eObject i also suggest to use (eObject[mainList[i].id])
which is faster than (eObject[mainList[i].id] !== undefined)
var i=mainList.length;
while(i--){
if (eObject[mainList[i].id]) {
//do something
}
}

Number of elements in a javascript object

Is there a way to get (from somewhere) the number of elements in a Javascript object?? (i.e. constant-time complexity).
I can't find a property or method that retrieves that information. So far I can only think of doing an iteration through the whole collection, but that's linear time.
It's strange there is no direct access to the size of the object, don't you think.
EDIT:
I'm talking about the Object object (not objects in general):
var obj = new Object ;
Although JS implementations might keep track of such a value internally, there's no standard way to get it.
In the past, Mozilla's Javascript variant exposed the non-standard __count__, but it has been removed with version 1.8.5.
For cross-browser scripting you're stuck with explicitly iterating over the properties and checking hasOwnProperty():
function countProperties(obj) {
var count = 0;
for(var prop in obj) {
if(obj.hasOwnProperty(prop))
++count;
}
return count;
}
In case of ECMAScript 5 capable implementations, this can also be written as (Kudos to Avi Flax)
function countProperties(obj) {
return Object.keys(obj).length;
}
Keep in mind that you'll also miss properties which aren't enumerable (eg an array's length).
If you're using a framework like jQuery, Prototype, Mootools, $whatever-the-newest-hype, check if they come with their own collections API, which might be a better solution to your problem than using native JS objects.
To do this in any ES5-compatible environment
Object.keys(obj).length
(Browser support from here)
(Doc on Object.keys here, includes method you can add to non-ECMA5 browsers)
if you are already using jQuery in your build just do this:
$(yourObject).length
It works nicely for me on objects, and I already had jQuery as a dependancy.
function count(){
var c= 0;
for(var p in this) if(this.hasOwnProperty(p))++c;
return c;
}
var O={a: 1, b: 2, c: 3};
count.call(O);
AFAIK, there is no way to do this reliably, unless you switch to an array. Which honestly, doesn't seem strange - it's seems pretty straight forward to me that arrays are countable, and objects aren't.
Probably the closest you'll get is something like this
// Monkey patching on purpose to make a point
Object.prototype.length = function()
{
var i = 0;
for ( var p in this ) i++;
return i;
}
alert( {foo:"bar", bar: "baz"}.length() ); // alerts 3
But this creates problems, or at least questions. All user-created properties are counted, including the _length function itself! And while in this simple example you could avoid it by just using a normal function, that doesn't mean you can stop other scripts from doing this. so what do you do? Ignore function properties?
Object.prototype.length = function()
{
var i = 0;
for ( var p in this )
{
if ( 'function' == typeof this[p] ) continue;
i++;
}
return i;
}
alert( {foo:"bar", bar: "baz"}.length() ); // alerts 2
In the end, I think you should probably ditch the idea of making your objects countable and figure out another way to do whatever it is you're doing.
The concept of number/length/dimensionality doesn't really make sense for an Object, and needing it suggests you really want an Array to me.
Edit: Pointed out to me that you want an O(1) for this. To the best of my knowledge no such way exists I'm afraid.
With jquery :
$(parent)[0].childElementCount

Categories