Michael Mikowski
Michael Mikowski

Reputation: 1279

JavaScript: Is there a better way to retain your array but efficiently concat or replace items?

I am looking for the best way to replace or add to elements of an array without deleting the original reference. Here is the set up:

var a = [], b = [], c, i, obj;
for ( i = 0; i < 100000; i++ ) { a[ i ] = i; b[ i ] = 10000 - i; }
obj.data_list = a;

Now we want to concatenate b INTO a without changing the reference to a, since it is used in obj.data_list. Here is one method:

for ( i = 0; i < b.length; i++ ) { a.push( b[ i ] ); }

This seems to be a somewhat terser and 8x (on V8) faster method:

a.splice.apply( a, [ a.length, 0 ].concat( b ) );

I have found this useful when iterating over an "in-place" array and don't want to touch the elements as I go (a good practice). I start a new array (let's call it keep_list) with the initial arguments and then add the elements I wish to retain. Finally I use this apply method to quickly replace the truncated array:

var keep_list = [ 0, 0 ];
for ( i = 0; i < a.length; i++ ){ 
  if ( some_condition ){ keep_list.push( a[ i ] );
}

// truncate array
a.length = 0;

// And replace contents
a.splice.apply( a, keep_list );

There are a few problems with this solution:

Has anyone found a better way?

Upvotes: 3

Views: 1652

Answers (1)

plalx
plalx

Reputation: 43718

Probably that the most comprehensive way yet still efficient would be to use push.

var source = [],
    newItems = [1, 2, 3];

source.push.apply(source, newItems);

If you ever reach the maximum call stack, you could seperate the operation into multiple batches.

var source = [],
    newItems = new Array(500000),
    i = 0,
    len = newItems.length,
    batch;

for (; i < len; i++) newItems[i] = i;

//You need to find a real cross-browser stack size, here I just used 50k
while((batch = newItems.splice(0, 50000)).length) {
    source.push.apply(source, batch);
}

console.log(source[499999]);

Also keep in mind that expensive operations might hang the browser, especially in old browsers that have slow JS engines. To circumvent the issue, you could further split the process into smaller batches and let the browser breath by using setTimeout.

Finally another approach I thought of would be to use a wrapper object around your array which would allow you to replace the array directly since your references would be retained through the object.

var arrWrapper = { list: [] },
    obj1 = { items: arrWrapper },
    obj2 = { items: arrWrapper };

//update the array
obj2.items.list = [1, 2, 3, 4];

//access the array
obj1.items.list;

The only restriction would be to avoid keeping a reference to arrWrapper.list directly.

Note: If you are targetting modern browsers only, you could probably make use of WebWorkers. However as far as I know, you can only pass serialized data which means that the worker wouldn't be able to modify the source array directly.

Upvotes: 1

Related Questions