Efficiently transferring elements between arrays - javascript

I have several JavaScript arrays, each containing a list of pointers to objects. When an object meets a certain condition, its pointer must be removed from its current containing array and placed into a different array.
My current (naive) solution is to splice out the exiting array elements and concatenate them onto the array they are entering. This is a slow method and seems to fragment memory over time.
Can anyone offer advice (general or JS-specific) on a better way to do this?
Demonstration code:
// Definitions
TestObject = function() {
this.shouldSwitch = function() {
return(Math.random() > 0.9);
}
}
A = [];
B = [];
while(A.length < 500) {
A.push(new TestObject());
}
// Transfer loop
doTransfers = function() {
var A_pending = [];
var B_pending = [];
for(var i = 0; i < A.length; i++) {
if(A[i].shouldSwitch()) {
B_pending.push(A[i]);
A.splice(i,1);
i--;
}
}
for(var i = 0; i < B.length; i++) {
if(B[i].shouldSwitch()) {
A_pending.push(B[i]);
B.splice(i,1);
i--;
}
}
A = A.concat(A_pending);
B = B.concat(B_pending);
}
setInterval(doTransfers,10);
Thanks!

For a language-independent kind of solution to this problem, when you're transferring elements from one contiguous sequence (array) to another, it's not appending elements to the back of the new array that's going to be bottlenecky (constant time complexity), it's going to be the removal of elements from the middle of your existing container (linear time complexity).
So the biggest benefit you can get is to replace that linear-time operation of removing from the middle of the array with a constant-time operation that still uses that cache-friendly, contiguous array representation.
One of the easiest ways to do this is to simply create two new arrays instead of one: a new array to append the elements you want to keep and a new array to append the elements you want to transfer. When you're done, you can swap out the new array of elements you want to keep (not transfer) with the old array you had.
In such a case, we're exchanging linear-time removals from the middle of a container with amortized constant-time insertions to the back of a new one. While insertion to the end of a container still has a worst-case complexity of O(N) for reallocations, it occurs infrequently enough and is still generally far better than paying for an operation that has an average complexity of O(N) every time you transfer a single element by constantly removing from the middle.
Another way to solve this problem that can be even more efficient, especially for certain cases like really small arrays since it only creates 1 new array, is this:
... when you transfer an element, first append a copy of it (possibly just a shallow copy) to the new container. Then overwrite the element at that index in the old container with the element from the back of the old container. Now simply pop off the element at the back of the old container. So we have one push, one assignment, and one pop.
In this case, we're exchanging a linear-time removal from the middle of a container with a single assignment (store/move instruction) and a constant-time pop from the back of the container (often basic arithmetic). This can work extremely well if the order of the elements in the old array can be shuffled around a little bit, and is often an overlooked solution for getting that linear-time removal from the middle of the array into one with constant-time complexity from the back of the array.

splice is pretty harmful for performance in a loop. But you don't seem to need mutations on the input arrays anyway - you are creating new ones and overwrite the previous values.
Just do
function doTransfers() {
var A_pending = [];
var B2_pending = [];
for (var i = 0; i < A.length; i++) {
if (A[i].shouldSwitch())
B_pending.push(A[i]);
else
A_pending.push(A[i]);
}
var B1_pending = [];
for (var i = 0; i < B.length; i++) {
if (B[i].shouldSwitch())
A_pending.push(B[i]);
else
B1_pending.push(B[i]);
}
A = A_pending;
B = B1_pending.concat(B2_pending);
}

Related

Is the space complexity O(1) or O(N) for the following code snippet?

This is one of the leetcode problems. In this problem we are to build an array from permutation and the challenge is to solve the problem in O(1) space complexity, so does the solution fulfills the criteria or not? One more thing if we are manipulating the same array but increasing its length by appending the elements at the end, does it mean that we are assigning new space hence causing O(N) space complexity during the execution of the program?
var buildArray = function(nums) {
let len = nums.length
for (let i = 0; i < len; i++) {
nums.push(nums[nums[i]]);
}
nums = nums.splice(len, nums.length);
return nums;
};
This is O(n) space complexity, you can't "cheat" by pushing your data on the input array, because you are using extra space anyways.
This code would be the equivalent of storing your data in a new array, and then returning that
I would guess the target of this space complexity limitation is for you to reach to a solution using pointers to mutate the input array
The space complexity still is O(n). The input array has n length. When you push in the array it basically allocates one more memory location and then update the array.
By pushing the elements in the array you are still using extra n space.
In C++, to resize the array code would be written as:
int* arr = new int[10]
int* resize_arr = new int[size*2];
for(int i = 0; i < size; i++)
resize_arr[i] = arr[i];
arr = resize_arr;
delete[] resize_arr;
after using all the allocated space, to add more elements you need to create a new array then copy the elements.
All these steps are done in one line in python. It does not mean you are not using more space.
Since for any given nums you are looping at least once the entire array and then do an O(1) operations ( keys lookup & push ), it would be safe to say this is an O(n) solution.

Array contains anything other than 0

I have an array of numbers with 64 indexes (it's canvas image data).
I want to know if my array contains only zero's or anything other than zero.
We can return a boolean upon the first encounter of any number greater than zero (even if the very last index is non-zero and all the others are zero, we should return true).
What is the most efficient way to determine this?
Of course, we could loop over our array (focus on the testImageData function):
// Setup
var imgData = {
data: new Array(64)
};
imgData.data.fill(0);
// Set last pixel to black
imgData.data[imgData.data.length - 1] = 255;
// The part in question...
function testImageData(img_data) {
var retval = false;
for (var i = 0; i < img_data.data.length; i++) {
if (img_data.data[i] > 0) {
retval = true;
break;
}
}
return retval;
}
var result = testImageData(imgData);
...but this could take a while if my array were bigger.
Is there a more efficient way to test if any index in the array is greater than zero?
I am open to answers using lodash, though I am not using lodash in this project. I would rather the answer be native JavaScript, either ES5 or ES6. I'm going to ignore any jQuery answers, just saying...
Update
I setup a test for various ways to check for a non-zero value in an array, and the results were interesting.
Here is the JSPerf Link
Note, the Array.some test was much slower than using for (index) and even for-in. The fastest, of course, was for(index) for(let i = 0; i < arr.length; i++)....
You should note that I also tested a Regex solution, just to see how it compared. If you run the tests, you will find that the Regex solution is much, much slower (not surprising), but still very interesting.
I would like to see if there is a solution that could be accomplished using bitwise operators. If you feel up to it, I would like to see your approach.
Your for loop is the fastest way on Chrome 64 with Windows 10.
I've tested against two other options, here is the link to the test so you can run them on your environment.
My results are:
// 10776 operations per second (the best)
for (let i = 0; i < arr.length; i++) {
if (arr[i] !== 0) {
break
}
}
// 4131 operations per second
for (const n of arr) {
if (n !== 0) {
break
}
}
// 821 operations per second (the worst)
arr.some(x => x)
There is no faster way than looping through every element in the array. logically in the worst case scenario the last pixel in your array is black, so you have to check all of them. The best algorithm therefore can only have a O(n) runtime. Best thing you can do is write a loop that breaks early upon finding a non-white pixel.

Why iterate by array with size faster

In first example I created empty array of length 1000:
var arr = new Array(1000);
for (var i = 0; i < arr.length; i++)
arr[i] = i;
In second example created empty array of length 0:
var arr = [];
for (var i = 0; i < 1000; i++)
arr.push(i);
Testing in Chrome 41.0.2272.118 on OS X 10.10.3 and first block run faster. Why? Because JavaScript-engine knows about array size?
Benchmark is here http://jsperf.com/poerttest/2.
If you don't specify the array size it will have to keep allocating more space. But if you specify the size at the beginning, it only allocates once.
Yes. When you allocate size, interpreter knows that it has allocate only 1000 element memory/space. So, when you insert element, it is just one operation. But when you declare dynamic array, 2nd scenario your case, interpreter has to increase size of the array and then push the element. It is 2 operations!
Another possibility could have been that push() is more expensive than assigning to a fixed position. But tests show it is not the case.
What happens is that empty arrays get a relatively small starting capacity (either hash pool or actual array), and increasing that pool is expensive. You can see that by trying with lower sizes: at 100 elements, the performance difference between Array(100) and [] disappears.

Efficient way to access members of Objects in JavaScript

My question somehow relates to this, but it still involves some key differences.
So here it is, I have following code;
for(var i = 0; i < someObj.children[1].listItems.length; i++)
{
doSomething(someObj.children[1].listItems[i]);
console.log(someObj.children[1].listItems[i]);
}
vs.
var i = 0,
itemLength = someObj.children[1].listItems.length,
item;
for(; i < itemLength; i++)
{
item = someObj.children[1].listItems[i];
doSomething(item);
console.log(item);
}
Now this is a very small exemplary part of code I deal with in an enterprise webapp made in ExtJS. Now here in above code, second example is clearly more readable and clean compared to first one.
But is there any performance gain involved when I reduce number of object lookups in similar way?
I'm asking this for a scenario where there'll be a lot more code within the loop accessing members deep within the object and iteration itself would be happening ~1000 times, and browser varies from IE8 to Latest Chrome.
There won't be a noticeable difference, but for performance and readability, and the fact that it does look like a live nodeList, it should probably be iterated in reverse if you're going to change it :
var elems = someObj.children[1].listItems;
for(var i = elems.length; i--;) {
doSomething(elems[i]);
console.log(elems[i]);
}
Performance gain will depend on how large the list is.
Caching the length is typically better (your second case), because someObj.children[1].listItems.length is not evaluated every time through the loop, as it is in your first case.
If order doesn't matter, I like to loop like this:
var i;
for( i = array.length; --i >= 0; ){
//do stuff
}
Caching object property lookup will result in a performance gain, but the extent of it is based on iterations and depth of the lookups. When your JS engine evaluates something like object.a.b.c.d, there is more work involved than just evaluating d. You can make your second case more efficient by caching additional property lookups outside the loop:
var i = 0,
items = someObj.children[1].listItems,
itemLength = items.length,
item;
for(; i < itemLength; i++) {
item = items[i];
doSomething(item);
console.log(item);
}
The best way to tell, of course, is a jsperf

What's the fastest way to keep an ordered map in javascript?

I use a javascript object as a map.
Let's say I populate it like this:
for (var i=0;i<100;i++) {
var key = "A"+(i%10);
oj[key] = i;
}
This creates a map with 10 keys.
The value of 100 and 10 are just fictitious. it could be 10000 events which create a map of 3000 or similar.
I now want to print the map alphabetically:
//
// First I transfer the items in an array.
//
array = [];
for (var i in oj) {
array.push(i);
}
//
// then I sort them
//
array.sort();
//
// now I can process them
//
str = '';
for (var i=0;i<array.length;i++) {
str+= array[i]+' '+oj[array[i]]+'\n';
}
Can anybody suggest a better* way ?
*better meaning faster !!!
Thanks a lot
Since you're creating the map, you can simultaneously create the sorted list (in sorted order). This would prevent both the array creation and the sorting at the times when you want to display them. You would end up trading memory for speed, but that is normal in performance tuning.
If you can't do the above all up front, consider sorting when you insert. Depending upon the sort implementation, that could save time.

Categories