Is Splice faster than Shift + Pop? Why? - actionscript-3
I've heard from some nonspecific sources that using a combination of shift() and pop() is faster than using splice(). I haven't run a benchmark test yet and will most likely not for a while since I'm out of town, but is this true, and if so, why?
If you don't need to retain the order of your Array, using .pop() is much, much faster than .splice().
.splice() will remove an element at a given index. What happens then is that every other element in the array after that one will need to have its position in the array reduced by 1. This can be slow if your array is large and the index at which you remove an element is small.
Using .pop(), you can remove this process of re-indexing everything entirely. Instead of removing an element at a given index, you can use .pop() to remove the last item from the array. From here, all you need to do is replace the item you want to remove with the one you got from using .pop(). As mentioned, this will of course mean that the order of your elements is not maintained.
Code examples:
.splice():
var list:Array = [0,1,2,3,4,5];
list.splice(2, 1);
trace(list); // 0,1,3,4,5
And then .pop():
var last:* = list.pop();
list[2] = last;
trace(list); // 0,1,5,4 - Notice we move 5 to the index 2 and lose the order.
And here we have the all-important actual performance tests:
var list:Array = [];
function fillList():void
{
for(var i = 0; i < 200000; i++) list.push(i);
}
function emptyViaSplice():void
{
fillList();
var t:Number = getTimer();
while(list.length > 0)
{
list.splice(0, 1);
}
trace("emptyViaSplice: " + (getTimer() - t));
}
function emptyViaPop():void
{
fillList();
var t:Number = getTimer();
while(list.length > 0)
{
if(list.length == 1) list.pop();
else
{
var l:* = list.pop();
list[0] = l;
}
}
trace("emptyViaPop: " + (getTimer() - t));
}
The results:
emptyViaSplice(); // 12153 ms
emptyViaPop(); // 37 ms
I tried another code and the result shows below:
var length = 200000;
function fillList() {
var list = [];
for(var i = 0; i < length; i++)
list.push(i);
return list;
}
function halfViaSplice() {
var list = fillList();
var startTime = new Date();
list.splice(length/2, length/2);
var endTime = new Date();
console.log("halfViaSplice: " + (endTime - startTime));
}
function halfViaPop() {
var list = fillList();
var startTime = new Date();
while(list.length > length/2) {
list.pop();
}
var endTime = new Date();
console.log("halfViaPop: " + (endTime - startTime));
}
halfViaSplice();
halfViaPop();
The results:
halfViaSplice: 0
halfViaPop: 4
I agree with Marty's answer, but if you remove all the last data from one index but not just remove in a hole position, I would advice splice than pop.
Classic algorithms sometimes are faster. Making no changes on the Array size is another way to achieve a very fast performance.
After several tests made with performance.now(), I found out the in the case below, this classic algorithm is faster than any method that changes array sizes and, like all javascript codes, gets faster with local vars.
var ArrayLength = Array.length; // always prefer the simplest references.
for (i=1; i < ArrayLength; i++) {Array[(i-1)] = Array[i];}
Array [ArrayLength-1] = [[newdata1],[newdata2]];
Compared in equal circumstances, the code above, has proven to be slightly faster than
Array.shift(); Array.push([[newdata1],[ newdata2]]);
For small arrays (20 to 60 indexes) that are cyclically changed, you should definitely consider classic algorithms.
Related
Simplest way to prevent math.random form selecting the same number twice (AS3)
I have a random number variable defined as below var rannum:Number = Math.floor(Math.random()*50+1); Then I have a trigger that calls for a new random number everytime a button is clicked ranbtn.addEventListener(MouseEvent.CLICK, reran); function reran (event:MouseEvent):void { rannum = Math.floor(Math.random()*50+1); } I would like to prevent the same random number from being selected until all the numbers have been selected and then possibly start over? I found a few threads like this one but none of them were specifically what I needed
You need to create an array of the possible values and each time you retrieve a random index from the array to use one of the values, you remove it from the array.Here you have an easy example with javascript. var uniqueRandoms = []; var numRandoms = 50; function makeUniqueRandom() { // refill the array if needed if (!uniqueRandoms.length) { for (var i = 0; i < numRandoms; i++) { uniqueRandoms.push(i); } } var index = Math.floor(Math.random() * uniqueRandoms.length); var val = uniqueRandoms[index]; // now remove that value from the array uniqueRandoms.splice(index, 1); return val; } I've found another option, You can declare an array of Integers:[1,2,3,4...50] and sort them randomly. var sorted:Array = []; for(var i:int = 0; i < 50; i++){ sorted.push(i); } //I'm making a copy of sorted in unsorted var unsorted:Array = sorted.slice(); //Randomly sort while(sorted.join() == unsorted.join()){ unsorted.sort(function (a:int, b:int):int { return Math.random() > .5 ? -1 : 1; }); }
If you get a selected num, you can add one until it is not selected.
Create a list of integers from 1 to 50. Pick a random integer from the list and remove it. When there are no more integers left (after 50 picks), repeat step 1. Code: function createRangeOfIntegers(from:int, to:int):Vector.<int> { if (from >= to) throw new ArgumentError("Invalid arguments"); var integers:Vector.<int> = new <int>[]; for (var i:int = from; i <= to; i++) { integers.push(i); } return integers; } function getRandomInteger(integers:Vector.<int>):int { var index:int = Math.random() * integers.length; var integer:int = integers.splice(index, 1)[0]; return integer; } Example: // create all the possible integers var integers:Vector.<int> = createRangeOfIntegers(1, 50); // select a random integer var random:int = getRandomInteger(integers); // When you've selected all integers you can start over if (integers.length == 0) integers = createRangeOfIntegers(1, 50);
Get each element from array as string actionscript3
I have an array like var test:Array = new Array("a", "b", "c"); How can I write a method to get one element and make it be string each time when I call this method. i.e when I call the method, it should return only 'a' and next time return only 'b' and so on.
You can use function shift of Array,here is a link about the function array shift var test:Array = new Array("a", "b", "c"); var firstLetter:String = test.shift();//"a" var secondLetter:String = test.shift();//"b" var thirdLetter:String = test.shift();//"c"
#Pan's answer is correct, but I feel the need to flag the fact that shift() ignites an extremely slow process of re-indexing your entire array. It's not something you need to concern yourself with with small arrays like in your example, but for larger arrays there's a significant performance boost if you reverse() the array first and then use pop(). I'll create a performance comparison below. Set up our test arrays: var shiftCopy:Array = []; var popCopy:Array = []; for(var i:int = 0; i < 100000; i++) { var rand:Number = Math.random() * i; shiftCopy.push('a' + rand); popCopy.push('a' + rand); } Run the tests: // Using shift. var t:int = getTimer(); while(shiftCopy.length > 0) shiftCopy.shift(); trace(getTimer() - t); // Using reverse and pop. t = getTimer(); popCopy.reverse(); while(popCopy.length > 0) popCopy.pop(); trace(getTimer() - t); My results: shift: 1651ms pop: 19ms
Sorting a multidimentional array and moving all the same values to the last array in as3
I'm currently working on a primitive graphic for the inventory that shows how much space is currently taken and how much theres left. It does this by showing 5 rows of 20 squares (Gray = free space, yellow =space taken). Now, my problem is that I want to find all values that are 1 and put them in the last array. So that the array goes from: var myMap:Array = [ [1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0], [1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0], [1,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0], [1,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0], [1,0,0,0,0,0,1,1,0,1,1,0,0,1,1,0,0,0,0,0] ]; to var myMap:Array = [ [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0], [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1] ]; Basically I want to sort the entire array so that all zero's come first and all 1's get put in the last one till its full, then the second last one and so on. I'm away I will most likely need an algorithm for this but I am hoping theres an easier way to do this. I'm usually a C# developer so AS3 is not my strongest suit. hopefully someone understood what I ment and can help me, its 4:30 am, so I might not be as lucid as I'd like. ~Thanks EDIT Added the code so people can get more of a informed look: Keep in mind most of the names and so on are placeholders, currently I just need it working. private function _showInventoryGraphic() { var mapWidth = 20; var mapHeight = 5; var myMap:Array = [ [1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0], [1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0], [1,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0], [1,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0], [1,0,0,0,0,0,1,1,0,1,1,0,0,1,1,0,0,0,0,0] ]; var posX:int = 15; var posY:int = 15; var startPosY:int = 250; for (var i:int=0; i<mapHeight; i++) { for (var u:int = 0; u < mapWidth; u++) { if (myMap[i][u] == 0) { var grayRect:Shape = new Shape; grayRect.graphics.beginFill(0x808080); grayRect.graphics.drawRect(posX*u, startPosY, 10,10); grayRect.graphics.endFill(); addChild(grayRect); } if (myMap[i][u] == 1) { var yellowRect:Shape = new Shape; yellowRect.graphics.beginFill(0xFFFF00); yellowRect.graphics.drawRect(posX*u, startPosY, 10,10); yellowRect.graphics.endFill(); addChild(yellowRect); } } startPosY += posY; } }
After trying it out from a few different approaches, I think the biggest challenge is the fact that it is a 2d array. The first attempt was similar, but like quicksort, where there were 2 pointers at either end of the 2d array and they moved 'inwards' looking for a scenario where the first pointer was a '1' and the last pointer was a '0' and swapping the two, until the pointers met in the 'middle'. I gave up on this method while trying to properly decrement and increment the linear counters in a 2d array. =b The second attempt was by keeping track of the last index in the array that was as empty and the last index in the array that was full. Then within the addItem and removeItem methods I'd put the item in the correct spot and update the counters to avoid a sort. This seemed to work, but maybe a bit messy and perhaps harder to maintain in the future. It sorts the result in separate arrays. In the third attempt for a 'direct sort', I think the most simple way then is just flatten the array apart in to a 2d array, sort it, then rejoin it. You would be correct in that this is not the most efficient method, though one thing to consider is how often this needs to run and how efficient it really needs to be given the context. I think a 2d array of only 100 elements is not too big of an issue to use an inefficient sort method? Maybe someone else can come up with better/cleaner methods. =b Below is code for what I mentioned. Second attempt (Note: I did not test this one fully, but it seemed to work): import flash.geom.Point; var unsortedMap:Array = [ [0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0] ]; var sortedMap:Array = [ [0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0] ]; var rowSize:int = unsortedMap[0].length; var rowCount:int = unsortedMap.length; // points to the last index at start, since want to have filled items at the 'end' var emptySpotPointer:Point = new Point(rowSize - 1, rowCount - 1); var fullSpotPointer:Point = new Point(rowSize - 1, rowCount - 1); function print2dArray(prefix:String, input:Array):void { trace(prefix); for(var row:String in input){ trace("\t"+ input[row]); } trace(""); } function addItem(inputX:int, inputY:int):void { if(unsortedMap[inputY][inputX] == 1){ trace("addItem() - Already an item here: "+ inputX +", "+ inputY); return; } trace("addItem() - Adding an item: "+ inputX +", "+ inputY); unsortedMap[inputY][inputX] = 1; sortedMap[emptySpotPointer.y][emptySpotPointer.x] = 1; fullSpotPointer.x = emptySpotPointer.x; fullSpotPointer.y = emptySpotPointer.y; if(emptySpotPointer.x == 0){ emptySpotPointer.x = rowSize - 1; emptySpotPointer.y--; } else { emptySpotPointer.x--; } } function removeItem(inputX:int, inputY:int):void { if(unsortedMap[inputY][inputX] == 0){ trace("removeItem() - No item here to remove: "+ inputX +", "+ inputY); return; } trace("removeItem() - Removing an item here: "+ inputX +", "+ inputY); unsortedMap[inputY][inputX] = 0; sortedMap[fullSpotPointer.y][fullSpotPointer.x] = 0; if(fullSpotPointer.x == (rowSize - 1)){ fullSpotPointer.x = 0; fullSpotPointer.y++; } else { fullSpotPointer.x++; } } // testing stuff here // ----------------------------------------------------------------- function addRandomitems():void { var randomX:int = Math.floor(Math.random() * rowSize); var randomY:int = Math.floor(Math.random() * rowCount); addItem(randomX, randomY); } function removeRandomitems():void { var randomX:int = Math.floor(Math.random() * rowSize); var randomY:int = Math.floor(Math.random() * rowCount); removeItem(randomX, randomY); } print2dArray("unsortedMap", unsortedMap); print2dArray("sortedMap", sortedMap); trace("Test: Adding items now ---------------------"); var counter:int = 0; for(counter = 0; counter < 50; counter++){ addRandomitems(); print2dArray("unsortedMap", unsortedMap); print2dArray("sortedMap", sortedMap); } trace("Test: Removing items now ---------------------"); for(counter = 0; counter < 50; counter++){ removeRandomitems(); print2dArray("unsortedMap", unsortedMap); print2dArray("sortedMap", sortedMap); } Third attempt: import flash.utils.getTimer; var myMap:Array = [ [1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0], [1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0], [1,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0], [1,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0], [1,0,0,0,0,0,1,1,0,1,1,0,0,1,1,0,0,0,0,0] ]; function sort(inputArray:Array):Array { var rowSize:int = inputArray[0].length; var flat:Array = new Array(); // flattening the array here for(var row:String in inputArray){ flat = flat.concat(inputArray[row]); } flat.sort(); var result:Array = new Array(); // recreating the original array here by cutting out 'rowSize' chunks and reforming the 2d array while(flat.length > 0){ result.push(new Array(flat.splice(0, rowSize))); } return result; } // testing var startTimer:int = flash.utils.getTimer(); for(var counter:int = 0; counter < 10000; counter++){ sort(myMap); } // Running the above 10,000 times takes 1836ms for me. trace(flash.utils.getTimer() - startTimer); // just to see the output: trace(sort(myMap).join("\n"));
Trouble creating a spectrogram
I know it was asked a thousand times before, but I still can't find a solution. Searching SO, I indeed found the algorithm for it, but lacking the mathematical knowledge required to truly understand it, I am helplessly lost! To start with the beginning, my goal is to compute an entire spectrogram and save it to an image in order to use it for a visualizer. I tried using Sound.computeSpectrum, but this requires to play the sound and wait for it to end, I want to compute the spectrogram in a way shorter time than that will require to listen all the song. And I have 2 hours long mp3s. What I am doing now is to read the bytes from a Sound object, the separate into two Vectors(.); Then using a timer, at each 100 ms I call a function (step1) where I have the implementation of the algorithm, as follows: for each vector (each for a channel) I apply the hann function to the elements; for each vector I nullify the imaginary part (I have a secondary vector for that) for each vector I apply FFT for each vector I find the magnitude for the first N / 2 elements for each vector I convert squared magnitude to dB scale end. But I get only negative values, and only 30 percent of the results might be useful (in the way that the rest are identical) I will post the code for only one channel to get rid off the "for each vector" part. private var N:Number = 512; private function step1() : void { var xReLeft:Vector.<Number> = new Vector.<Number>(N); var xImLeft:Vector.<Number> = new Vector.<Number>(N); var leftA:Vector.<Number> = new Vector.<Number>(N); // getting sample range leftA = this.channels.left.slice(step * N, step * (N) + (N)); if (leftA.length < N) { stepper.removeEventListener(TimerEvent.TIMER, getFreq100ms); return; } else if (leftA.length == 0) { stepper.removeEventListener(TimerEvent.TIMER, getFreq100ms); return; } var i:int; // hann window function init m_win = new Vector.<Number>(N); for ( var i:int = 0; i < N; i++ ) m_win[i] = (4.0 / N) * 0.5 * (1 - Math.cos(2 * Math.PI * i / N)); // applying hann window function for ( i = 0; i < N; i++ ) { xReLeft[i] = m_win[i]*leftA[i]; //xReRight[i] = m_win[i]*rightA[i]; } // nullify the imaginary part for ( i = 0; i < N; i++ ) { xImLeft[i] = 0.0; //xImRight[i] = 0.0; } var magnitutel:Vector.<Number> = new Vector.<Number>(N); fftl.run( xReLeft, xImLeft ); current = xReLeft; currf = xImLeft; for ( i = 0; i < N / 2; i++ ) { var re:Number = xReLeft[i]; var im:Number = xImLeft[i]; magnitutel[i] = Math.sqrt(re * re + im * im); } const SCALE:Number = 20 / Math.LN10; var l:uint = this.total.length; for ( i = 0; i < N / 2; i++ ) { magnitutel[i] = SCALE * Math.log( magnitutel[i] + Number.MIN_VALUE ); } var bufferl:Vector.<Number> = new Vector.<Number>(); for (i = 0; i < N / 2 ; i++) { bufferl[i] = magnitutel[i]; } var complete:Vector.<Vector.<Number>> = new Vector.<Vector.<Number>>(); complete[0] = bufferl; this.total[step] = complete; this.step++; } This function is executed in the event dispatched by the timer (stepper). Obviously I do something wrong, as I said I have only negative values and further more values range between 1 and 7000 (at least). I want to thank you in advance for any help. With respect, Paul
Negative dB values are OK. Just add a constant (representing your volume control) until the number of points you want to color become positive. The remaining values that stay negative are usually just displayed or colored as black in a spectrogram. No matter how negative (as they might just be the FFT's numerical noise, which can be a huge negative dB number or even NaN or -Inf for log(0)).
How to remove/reload movieclip/graphic in Flash CS5 with AS3
I have a setupBoard(); and a setupBlocks(); in my function: function init(e) { setupBoard(); removeEventListener(Event.ENTER_FRAME , init); setupCat(); setupBlocks(); } function setupBoard() { var columns:Array = new Array(); var i,j:int; var _place:place; for (i = 0; i < 11; i++) { columns = []; for (j = 0; j < 11; j++) { _place = new place(); _place.thisX=i; _place.thisY=j; _place.thisDistance=Math.min(i+1,j+1,11-i,11-j)*11; _place.y = 56 * i + 3; _place.x = 5 + 71 * j + 35*(i%2); _place.buttonMode=true; _place.addEventListener(MouseEvent.CLICK, setBlock); columns[j] = _place; // SÆTTER TAL PÅ BRIKKERNE _place.thisText.text = _place.thisDistance + " - " + _place.thisX + " : " + _place.thisY; addChild(_place); } rows[i] = columns; } } The "place" is the MovieClip this function loads when the game launches and when the game is finish/completed.. the setupBoard, setup the board ofc, and the setupBlocks setup some movieclips, which contain some graphic. Here's my question, how do I remove/reload all the blocks when the game enters that function again? At the moment they are just placed upon each other, which I don't like at all.
If I understood correctly, what you want to do is remove all the previous blocks (from the last time you ran the setup function) when you run setup a second time. To do that, you should create a function which loops your rows and columns Arrays, and for each Place object it find, it does the following: removes it from the stage, removes all Event Listeners, and finally sets it to null. Your function could look something like this (and you would call it just before calling setup again): for (i = 0; i < rows.length; i++) { var column:Array = rows[i]; for (j = 0; j < column.length; j++) { var place:Place = column[j]; if (contains(place)) { removeChild(place); } place.removeEventListener(MouseEvent.CLICK, setBlock); place = null; } column = []; } row = []; I just wrote that straight into the box, so it's not tested. But basically it does the three things necessary to get those objects removed from the view, and clears up anything that would stop them from being freed from memory by the garbage collector. Hope that helps. Debu