If you want more speed, handle allready the ids as integers, not array, and have bit changed with & and | operations :
counts = {};
var a = [1,0,1,0,0,3];
for(var i=0; i<a.length; i++ ){
var c = counts[a[i]] || 0 ;
counts[a[i]]= c+1 ;
}
console.log(counts); // { 0: 3, 1: 2, 3: 1 }
to set the k-th bit :
id |= 1 << k;
to clear the k-th bit :
id &= !(1 << k);
to swap the k-th bit :
id ^= (1 << k);
to read the k-th bit :
bitK = (id >> k ) & 1;
So you might write small functions to do those operations, with a 99% chance they will get
inlined by the javascript engine, it will be much faster than array handling + parseInt.
You might even write this code directly. Maybe in some case you will be able to
cache 1 << k, but anyway, shift instructions have a very little cost.
Another advantage is that you can handle several tests in a single instruction,
for instance :
var setRequiredMask = ( 1<<3 | 1<<5 ) ; // to test if bits 3 and 5 set
var clearRequiredMask = ( 1<<2 | 1 <<4 ) ; // to test if bit 2 and 4 cleared
var someRequiredMask = ( 1<<0 | 1 <<6 ) ; // to test if either bit 0 or bit 6 to be set
var satisfyConditions = ((id & setRequiredMask) == setRequiredMask) &&
((id & !clearRequiredMask) ==0 ) &&
(id & someRequiredMask) ;
Here we trade 7 access to memory to quite the same number of boolean operations.
If this test is used in a loop to filter an array, the speed gain is huge.
In the same way you can set / clear / swap several bits in a single instruction, test if one condition set is included in the other, test if they are orthogonal, .... all in a very fast way.
Only limit is that you can handle no more than 32 possibilities with this method.
Then you can store the ids in a typed a typed array, depending on the max number
of possibilities, use only a Int8Array or a Int32Array, but i am not sure the gain
in performance will be that interesting compared to [].
Syntax :
var arr = new Int8Array( _length_ );
or
var arr = new Int32Array( _length_ );