I'm creating a few specific functions for a compiler I'm working on, But certain restrictions within the compiler's nature will prevent me from using native JavaScript methods like Array.prototype.pop() to perform array pops...
So I decided to try and write some rudimentary pseudo-code to try and mimic the process, and then base my final function off the pseudo-code... But my tests seem to fail... based on the compiler's current behavior, it will only allow me to use array.length, array element assignments and that's about it... My code is below...
pop2 = function(arr) {
if(arr.length>0){
for(var w=undefined,x=[],y=0,z=arr.length;y<=z;y++){
y+1<z?(x[y]=arr[y]):(w=arr[y],arr=x);
}
}
return w;
}
Arr = [-1,0,1,2];
// Testing...
console.log(pop2(Arr)); // undefined... should be 2
console.log(Arr); // [-1,0,1,2]... should be [-1,0,1]
I'm trying to mimic the nature of the pop function but can't seem to put my finger on what's causing the function to still provide undefined and the original array... undefined should only return if an initial empty array is sent, just like you would expect with a [].pop() call...
Anyone have any clues as to how I can tailor this code to mimic the pop correctly?
And while I have heard that arr.splice(array.length-1,1)[0]; may work... the compiler is currently not capable of determining splice or similar methods... Is it possible to do it using a variation of my code?
Thanks in advance...
You're really over-thinking [].pop(). As defined in the specs, the process for [].pop() is:
Get the length of the array
If the length is 0
return undefined
If length is more than 0
Get the item at length - 1
Reduce array.length by 1
Return item.
(... plus a few things that the JavaScript engine needs to do behind the scenes like call ToObject on the array or ensure the length is an unsigned 32-bit integer.)
This can be done with a function as simple as the one below, there's not even a need for a loop.
function pop(array) {
var length = array.length,
item;
if (length > 0) {
item = array[length - 1];
array.length -= 1;
}
return item;
}
Edit
I'm assuming that the issue with the compiler is that Array.prototype.pop isn't understood at all. Re-reading your post, it looks like arrays have a pop method, but the compiler can't work out whether the variable is an array or not. In that case, an even simpler version of this function would be this:
function pop(array) {
return Array.prototype.pop.call(array);
}
Try that first as it'll be slightly faster and more robust, if it works. It's also the pattern for any other array method that you may need to use.
With this modification, it works:
http://jsfiddle.net/vxxfxvpL/1/
pop2 = function(arr) {
if(arr.length>0){
for(var w=undefined,x=[],y=0,z=arr.length;y<=z;y++){
if(y+1<z) {
(x[y]=arr[y]);
} else {
(w=arr[y],arr=x);
break;
}
}
}
return w;
}
Arr = [-1,0,1,2];
// Testing...
console.log(pop2(Arr)); // 2
The problem now is to remove the last element. You should construct the original array again without last element. You will have problems with this because you can't modify the original array. That's why this tasks are maded with prototype (Array.prototype.pop2 maybe can help you)
I've been trying to convert the following JS code that uses a for loop to a while loop and/or do-while loop.
var unique = function(array)
{
var newArray = []
array.sort()
for(var x in array) if(array[x] != array[x-1]) newArray.push(array[x])
return newArray
}
The code is suppose to return only distinct names from an array of names that repeat. I've been trying to convert the for loop but so far I've been running into problems using this:
do
{
newArray.push(array[x])
}
while(array[x] != array[x-1])
return newArray;
Can anyone help me? Thanks!
Is it guaranteed that your names are only going to be duplicated in sequence i.e. is it true that if a name does have a duplicate it will be directly after it? If not, then checking the elements that are directly next to each will not find all the duplicates. You'll have to do a nested for loop or some other n^2 algorithm.
var duplicated = false;
for (int x = 0; x < array.length; x++)
{
for (int y = 0; y < array.length; y++)
{
if (array[x] == array[y])
{
duplicated = true;
}
}
if (!duplicated)
{
array.push(array[x]);
}
duplicated = false;
}
return newArray;
please note, this implementation is very poor but it gets the point across.
You're very close. The following preserves the original sequence:
function getUnique(array) {
var newArray = array.slice(); // copy original
var i = newArray.length - 1;
do {
if (newArray[i] == newArray[--i]) {
newArray.splice(i, 1);
}
} while(i)
return newArray;
}
Note that the above assumes a sorted, contiguous array (no missing members). If you can't be sure of that, sort newArray before the do..while loop and maybe compact it to make it contiguous.
A while loop doesn't make sense for your use case. A while loop is good when you want to loop as long as some condition is met and then stop the first time it fails. But a for loop is good when you want to loop through a certain number of items (for instance all of them).
So stick with a for loop. Also a few notes on while loops for whenever you do use them:
A for loop automatically updates the loop index which in your case is x. A while loop doesn't. So, to replicate your for loop, you would need to manually increment x.
Also, you test at the top of the for loop. To mirror that behavior you would want a while instead of a do- while (while tests before executing each loop, do-while tests after).
But if you used a while loop, you'd exit the loop the first time array[x] != array[x-1] failed. And it sounds like you don't want that. You want to push all values that meet that test.
Is there an alternative, faster method of returning the position/index of part of an array within another array (where multiple values match)? It's called a lot within my pathfinding algorithm so could do with being as fast as possible.
My current function is:
// Haystack can be e.g. [[0,1,278.9],[4,4,22.1212]]
function coordinate_location_in_array(needle,haystack){
for(n in haystack){
if(haystack[n][0]==needle[0] && haystack[n][1]==needle[1]) return n;
}
return false;
}
// Needle of [0,1]: returns 0
// Needle of [4,4]: returns 1
// Needle of [6,7]: returns false
Edit:
I've been messing around a bit and come up with a (rather ghastly) string manipulation-based method (thereby avoiding the costly for loop). I think it's still slightly slower. Could anybody benchmark these methods?
function coordinate_location_in_array(needle,haystack) {
var str1 = ':' + haystack.join(':');
var str2 = str1.replace(':'+needle[0]+','+needle[1],'*').split('*')[0];
if(str2.length == str1.length) return false;
var preceedingElements = str2.match(/:/g);
return preceedingElements!=null?preceedingElements.length:0;
}
Perhaps with some improvements this second method might provide some performance gain?
Edit 2:
Bench marked all 3 described methods using jsperf.com (initial method is fastest):
http://jsperf.com/finding-matched-array-within-array/3
Edit 3:
Just replaced the for(..in..) loop with a for(..;..;..) loop (since I know that the haystack array will never have "gaps") and performance seems to have significantly improved:
function coordinate_location_in_array(needle,haystack){
for(var n=0;n<haystack.length;n++){
if(haystack[n][0]==needle[0] && haystack[n][1]==needle[1]) return n;
}
return false;
}
I've updated the jsperf page to include this latest method.
If the "haystack" isn't sorted then there isn't a way to make it faster. Not knowing how the elements in a collection are ordered makes finding something out of it linear by nature, because you just have to check each thing.
If you are using this function over the same "haystack" over and over, you could sort the collection, and use the sorting to make it quicker to find the "needle" (look up different sorting and search algorithms to find one that fits your need best, such as using binary search to find the "needle" after haystack is sorted.)
i don't know if its faster, but you can do something like:
[1,2,3,4].slice(0,2).toString() == [1,2].toString()
in your case it would be:
function coordinate_location_in_array(needle,haystack){
for(n in haystack){
if(haystack[n].slice(0,2).toString() == needle.toString()) return n
}
return false;
}
Also found this post, which covers comparison of JS arrays: compare-two-arrays-javascript-associative
Cheers
Laidback
Using a for(..;..;..) loop rather than a for(..in..) loop made the biggest difference.
(See Edit 3 at the end of the question)
Seems to me this is just a substring search but with numbers instead of characters being the components of the string. As such, Boyer-Moore could be applicable, especially if your needles and haystacks get big.
array.sort(function(left, right) {
return index(otherArray, left) < index(otherArray, right);
});
This is O(len(array) ^ 2) so for a reasonable size array of len = 1000 this takes constant * 1 million operations which easily overshoots the IE 5 million operators cap.
Thus IE throws a script is taking too long even though this is fast.
The problem is that IE does not have it's own Array.prototype.indexOf so I can't reduce the operation count down to O(len(array) and rely instead end up using a double for loop instead of a single for loop.
I considered array.join and using String.prototype.indexOf but the objects in the arrays are DOM elements and you can't convert them to a string (easily).
Telling IE users to remove this default cap is not an option.
I can think of two possible solutions to this problem: one of which will work everywhere, the other which is entirely IE-proprietary (and I expect doesn't work in IE9, but that supports Array.prototype.indexOf, so that's a non-issue).
The first, simpler, solution is to just set a property on each HTMLElement of the desired order and sort by that. If you care about the desired order persisting, you'll have to make sure the HTMLElement objects don't get garbage collected, so you'll have to keep references to them around (it's probably simplest to just create an array in the global scope for it).
The IE-only solution is to do something similar to what #maclema was proposing, using a lookup object, and HTMLElement.uniqueID:
var otherArrayLookup = {};
for (var i=0; i < otherArray.length; i++) {
otherArrayLookup[otherArray[i].uniqueID] = i;
}
array.sort(function(left, right) {
return otherArrayLookup[left.uniqueID] < otherArrayLookup[right.uniqueID];
});
You'll want to add some branches in there (don't put any within the callback function, but use different callback functions) for the Array.prototype.indexOf supported case, the HTMLElement.uniqueID supported case, and the none-of-the-above case.
You could try making an index lookup object. This should greatly increase performance too.
var otherArrayLookup = {};
for ( var i=0; i<otherArray.length; i++ ) {
otherArrayLookup[otherArray[i]] = i;
}
array.sort(function(left, right) {
return otherArrayLookup[left] < otherArrayLookup[right];
});
In the past and with most my current projects I tend to use a for loop like this:
var elements = document.getElementsByTagName('div');
for (var i=0; i<elements.length; i++) {
doSomething(elements[i]);
}
I've heard that using a "reverse while" loop is quicker but I have no real way to confirm this:
var elements = document.getElementsByTagName('div'),
length = elements.length;
while(length--) {
doSomething(elements[length]);
}
What is considered as best practice when it comes to looping though elements in JavaScript, or any array for that matter?
Here's a nice form of a loop I often use. You create the iterated variable from the for statement and you don't need to check the length property, which can be expensive specially when iterating through a NodeList. However, you must be careful, you can't use it if any of the values in array could be "falsy". In practice, I only use it when iterating over an array of objects that does not contain nulls (like a NodeList). But I love its syntactic sugar.
var list = [{a:1,b:2}, {a:3,b:5}, {a:8,b:2}, {a:4,b:1}, {a:0,b:8}];
for (var i=0, item; item = list[i]; i++) {
// Look no need to do list[i] in the body of the loop
console.log("Looping: index ", i, "item" + item);
}
Note that this can also be used to loop backwards.
var list = [{a:1,b:2}, {a:3,b:5}, {a:8,b:2}, {a:4,b:1}, {a:0,b:8}];
for (var i = list.length - 1, item; item = list[i]; i--) {
console.log("Looping: index ", i, "item", item);
}
ES6 Update
for...of gives you the name but not the index, available since ES6
for (const item of list) {
console.log("Looping: index ", "Sorry!!!", "item" + item);
}
Note that in some cases, you need to loop in reverse order (but then you can use i-- too).
For example somebody wanted to use the new getElementsByClassName function to loop on elements of a given class and change this class. He found that only one out of two elements was changed (in FF3).
That's because the function returns a live NodeList, which thus reflects the changes in the Dom tree. Walking the list in reverse order avoided this issue.
var menus = document.getElementsByClassName("style2");
for (var i = menus.length - 1; i >= 0; i--)
{
menus[i].className = "style1";
}
In increasing index progression, when we ask the index 1, FF inspects the Dom and skips the first item with style2, which is the 2nd of the original Dom, thus it returns the 3rd initial item!
I like doing:
var menu = document.getElementsByTagName('div');
for (var i = 0; menu[i]; i++) {
...
}
There is no call to the length of the array on every iteration.
I had a very similar problem earlier with document.getElementsByClassName(). I didn't know what a nodelist was at the time.
var elements = document.getElementsByTagName('div');
for (var i=0; i<elements.length; i++) {
doSomething(elements[i]);
}
My issue was that I expected that elements would be an array, but it isn't. The nodelist Document.getElementsByTagName() returns is iterable, but you can't call array.prototype methods on it.
You can however populate an array with nodelist elements like this:
var myElements = [];
for (var i=0; i<myNodeList.length; i++) {
var element = myNodeList[i];
myElements.push(element);
};
After that you can feel free to call .innerHTML or .style or something on the elements of your array.
At the risk of getting yelled at, i would get a javascript helper library like jquery or prototype they encapsulate the logic in nice methods - both have an .each method/iterator to do it - and they both strive to make it cross-browser compatible
EDIT: This answer was posted in 2008. Today much better constructs exist. This particular case could be solved with a .forEach.
I think using the first form is probably the way to go, since it's probably by far the most common loop structure in the known universe, and since I don't believe the reverse loop saves you any time in reality (still doing an increment/decrement and a comparison on each iteration).
Code that is recognizable and readable to others is definitely a good thing.
I too advise to use the simple way (KISS !-)
-- but some optimization could be found, namely not to test the length of an array more than once:
var elements = document.getElementsByTagName('div');
for (var i=0, im=elements.length; im>i; i++) {
doSomething(elements[i]);
}
Also see my comment on Andrew Hedges' test ...
I just tried to run a test to compare a simple iteration, the optimization I introduced and the reverse do/while, where the elements in an array was tested in every loop.
And alas, no surprise, the three browsers I tested had very different results, though the optimized simple iteration was fastest in all !-)
Test:
An array with 500,000 elements build outside the real test, for every iteration the value of the specific array-element is revealed.
Test run 10 times.
IE6:
Results:
Simple: 984,922,937,984,891,907,906,891,906,906
Average: 923.40 ms.
Optimized: 766,766,844,797,750,750,765,765,766,766
Average: 773.50 ms.
Reverse do/while: 3375,1328,1516,1344,1375,1406,1688,1344,1297,1265
Average: 1593.80 ms. (Note one especially awkward result)
Opera 9.52:
Results:
Simple: 344,343,344,359,343,359,344,359,359,359
Average: 351.30 ms.
Optimized: 281,297,297,297,297,281,281,297,281,281
Average: 289.00 ms
Reverse do/while: 391,407,391,391,500,407,407,406,406,406
Average: 411.20 ms.
FireFox 3.0.1:
Results:
Simple: 278,251,259,245,243,242,259,246,247,256
Average: 252.60 ms.
Optimized: 267,222,223,226,223,230,221,231,224,230
Average: 229.70 ms.
Reverse do/while: 414,381,389,383,388,389,381,387,400,379
Average: 389.10 ms.
Form of loop provided by Juan Mendez is very useful and practical,
I changed it a little bit, so that now it works with - false, null, zero and empty strings too.
var items = [
true,
false,
null,
0,
""
];
for(var i = 0, item; (item = items[i]) !== undefined; i++)
{
console.log("Index: " + i + "; Value: " + item);
}
I know that you don't want to hear that, but: I consider the best practice is the most readable in this case. As long as the loop is not counting from here to the moon, the performance-gain will not be uhge enough.
I know this question is old -- but here's another, extremely simple solution ...
var elements = Array.from(document.querySelectorAll("div"));
Then it can be used like any, standard array.
I prefer the for loop as it's more readable. Looping from length to 0 would be more efficient than looping from 0 to length. And using a reversed while loop is more efficient than a foor loop as you said. I don't have the link to the page with comparison results anymore but I remember that the difference varied on different browsers. For some browser the reversed while loop was twice as fast. However it makes no difference if you're looping "small" arrays. In your example case the length of elements will be "small"
I think you have two alternatives. For dom elements such as jQuery and like frameworks give you a good method of iteration. The second approach is the for loop.
I like to use a TreeWalker if the set of elements are children of a root node.