Restricted JavaScript Array Pop Polyfill not working - javascript

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)

Related

Strange issue in simple Javascript code, non-empty array parameter reaches function empty

I've been practising JS/ECMAScript lately with examples that I find on the net and while trying one about reversing arrays, I've hit a really strange problem. The code that I put below is complete but half-working.
"use strict";
function reverseArray(theArray){
let tempArray = [];
if(theArray && theArray.length > 0){
console.log('Here works!');
while(theArray.length > 0){
tempArray.unshift(theArray.shift());
}
}
return tempArray;
}
function reverseArrayInPlace(theArray){
console.log(theArray);
if(theArray && theArray.length > 0){
console.log('Should go through here!');
for(let i = 0; i < Math.floor(theArray.length/2); i++){
let temp = theArray[i];
theArray[i] = theArray[theArray.length - 1 - i];
theArray[theArray.length -1 - i] = temp;
}
}
return theArray;
}
let firstSacrifice = ['I','n','v','e','r','t','M','E'];
let secondSacrifice = ['I','n','v','e','r','t','M','E','A','g','a','i','N'];
console.log(firstSacrifice);
console.log('InvertME >>', reverseArray(firstSacrifice));
console.log('InvertME >>', reverseArrayInPlace(firstSacrifice));
console.log('\n');
console.log(secondSacrifice);
console.log('InvertMEAgaiN >>', reverseArray(secondSacrifice));
console.log('InvertMEAgaiN >>', reverseArrayInPlace(secondSacrifice));
The first function, reverseArray, works as expected: it copies the given array into another. The problem comes with the second function, reverseArrayInPlace. In theory, it should do the reversal in the same given array it receives but, strangely, the parameter theArray reaches that function empty []. Therefore, no reversal done with that function at all.
I'm testing the code on a shell with node (v6.10), but in this case I also executed it in the consoles of firefox, chrome and edge with the very same result. For some reason that I cannot fathom (and as usual with this kind of silly errors, I've been trying for a good few hours), the parameter theArray is empty on arrival and, of course, no errors or warnings are given at all.
By the way, I found the exercise proposed in this tutorial about Javascript. I know it's a bit old (and yes, I've already read the docs about JS on MDN) but the exercises proposed there are interesting enough for practice's sake.
Oh, and click here if you want to fiddle with this code, but remember to open the console to see the log entries!
Thanks!
The first function, reverseArray, works as expected: it copies the given array into another.
No, it does not. Instead of copying the elements, it moves them - which is surely not what you intended. Calling shift upon the array will empty it - you even do that explicitly while (theArray.length > 0) - i.e. until its length is 0!
To copy the array, you would use something like
function reverseArray(theArray){
let tempArray = [];
for (var i=theArray.length; i--; )
tempArray.push(theArray[i]);
return tempArray;
}
(though there are many other ways to do it, e.g. return theArray.slice().reverse() using builtins)
Arrays (and all other javascript objects) are passed by their references not by their values. So in the first call the array is emptied so the next call will recieve an empty array! Thus in those function you should not alter the array passed as parametter. Try this alternative:
function reverseArray(theArray){
let tempArray = [];
if(theArray){
let n = theArray.length; // use a local variable to tell which item from the array to move
while(n){ // while there is still items in theArray to move
tempArray.push(theArray[--n]); // decrement n and add its corresponding value to tempArray (here theArray is not altered at all)
}
}
return tempArray;
}
The problem is this:
tempArray.unshift(theArray.shift());
Specifically:
theArray.shift();
The .shift() method modifies the array in-place by removing the element shifted from the array. So after calling reverseArray() the original array would be empty.
Alternatively the real bug could be with the function name. It should not be called reverseArray but should instead be called returnNewReversedArrayAndEmptyArray.

Run the same function on each item in a list / array

Goal
I have a working function (JSFiddle). On numerous occasions throughout a script the function runs sequentially. In these instances, there is a lot of repetitious code that I would like to consolidate.
Ideally changing code like this:
functionName("First_item") +
functionName("Second_item") +
functionName("Third_item") +
To something like this:
functionName("First_item", "Second_item", "Third_item");
The function will run for each item in the list so the result is the same but the code more elegant and maintainable.
Notes:
I’m not looking to use any libraries (e.g. jQuery) to accomplish the goal.
Solution
Amit Joki’s answer kindly noted I could use arguments. When I implemented the code, the modified function (JSFiddle) only returned the output string for the first argument / item.
Vanice’s answer pointed out the eventual solution.
Make one string from the output of all arguments / items by concatenating (joining) the output strings within the for loop (with the use of +=).
Return the concatenated output by placing the return outside of the for loop.
Example
Working solution (JSFiddle).
Thanks
Thank you very much to everyone for their time and help on this. I really appreciate it!
Leveraging Javascript's Prototype OOP: You can add an each function to Array's themselves, so every array in your code that will automatically have an inhereted each function.
Array.prototype.each = function(callback){
for (var i = 0; i < this.length; i++){
callback(this[i]);
}
}
Usage:
myArray.each(myCoolFunction)
['something','somethingelse',somethingother'].each(myCoolFunction)
myArray.each( function (item) {
// if your item has a method
item.Something();
// if you'd like to call a function on the item:
doSomething(item);
});
caveats:
Because javascript is an asynchronous language that is interpreted differently across various browsers and inherently handles primitive objects and complex objects differently, it is highly recommended usage of underscore or lodash. You can also make your own, but you'll need ensure the objects passed through will be handled by the functions properly. This may include workarounds or creating special callback functions for different object types that are passed through your each function.
For more information: Is JavaScript a pass-by-reference or pass-by-value language?
Libraries you should seriously consider:
lodash:
https://lodash.com/docs#forEach
_([1, 2, 3]).forEach(function(num) { console.log(num); }).join(',');
// → logs each number and returns '1,2,3'
_.forEach({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { console.log(num); });
// → logs each number and returns the object (property order is not guaranteed across environments)
underscore:
http://underscorejs.org/#each
_.each([1, 2, 3], alert);
=> alerts each number in turn...
You don't need an array. Just use arguments
function functionName(){
for(var i = 0; i < arguments.length; i++){
// do something with arguments[i];
}
}
and then you can do
functionName("shot_type","shot_height","shot_angle","framed","scene_depth");
P.S #codebox's solution works if supporting legacy IE version isn't a problem. Don't know why he deleted it...so putting it here so it helps. His answer using forEach
["shot_type","shot_height","shot_angle","framed","scene_depth"].forEach(FunctionName);
EDIT: Looking at your Fiddle, you have a return inside the for loop - therefore the function will return after the first iteration. Put the return after the for and concatenate the output to one string.
var output = "";
for(...){
output += description_of_object + ": " + randomly_selected_item_from_object + ".\n";
}
// return it
return output;
With Javascript only:
var actions = ["shot_type","shot_height","shot_angle","framed","scene_depth"];
for (var i = 0; i < actions.length; i++){
FunctionName(actions[i]);
}
With JQuery:
$.each(["shot_type","shot_height","shot_angle","framed","scene_depth"], function(index,value){
FunctionName(value);
});
I haven't tested it but it should work.
To avoide redundancy in code use an array with the values, that you want to pass through the function and call the function in an loop.
var vals=["shot_type","shot_height","shot_angle","framed","scene_depth"];
for(var i=0; i<vals.length; i++)
{
FunctionName(vals[i]);
}
If you want to expand the function (adding another parameter) you can just expand the for-loop and the array structure.
Alternatively you could fill an object with the values and handle this logic in an object. But this would just do a difference on calling the function.

Programming optional ignorance

In Javascript what is the best way to handle scenarios when you have a set of arrays to perform tasks on sets of data and sometimes you do not want to include all of the arrays but instead a combination.
My arrays are labeled in this small snippet L,C,H,V,B,A,S and to put things into perspective the code is around 2500 lines like this. (I have removed code notes from this post)
if(C[0].length>0){
L=L[1].concat(+(MIN.apply(this,L[0])).toFixed(7));
C=C[1].concat(C[0][0]);
H=H[1].concat(+(MAX.apply(this,H[0])).toFixed(7));
V=V[1].concat((V[0].reduce(function(a,b){return a+b}))/(V[0].length));
B=B[1].concat((MAX.apply(this,B[0])-MIN.apply(this,B[0]))/2);
A=A[1].concat((MAX.apply(this,A[0])-MIN.apply(this,A[0]))/2);
D=D[1].concat((D[0].reduce(function(a,b){return a+b}))/(D[0].length));
S=S[1].concat((S[0].reduce(function(a,b){return a+b}))/(S[0].length));
}
It would seem counter-productive in this case to litter the code with tones of bool conditions asking on each loop or code section if an array was included in the task and even more silly to ask inside each loop iteration with say an inline condition as these would also slow down the processing and also make the code look like a maze or rabbit hole.
Is there a logical method / library to ignore instruction or skip if an option was set to false
All I have come up with so far is kind of pointless inline thing
var op=[0,1,1,0,0,0,0,0]; //options
var L=[],C=[],H=[],V=[],B=[],A=[],D=[],S=[];
op[0]&&[L[0]=1];
op[1]&&[C[0]=1,console.log('test, do more than one thing')];
op[2]&&[H[0]=1];
op[3]&&[V[0]=1];
op[4]&&[B[0]=1];
op[5]&&[A[0]=1];
op[6]&&[A[0]=1];
It works in that it sets only C[0] and H[0] to 1 as the options require, but it fails as it needs to ask seven questions per iteration of a loop as it may be done inside a loop. Rather than make seven versions of the the loop or code section, and rather than asking questions inside each loop is there another style / method?
I have also noticed that if I create an array then at some point make it equal to NaN rather than undefined or null the console does not complain
var L=[],C=[],H=[],V=[],B=[],A=[],D=[],S=[];
L=NaN;
L[0]=1;
//1
console.log(L); //NaN
L=undefined;
L[0]=1
//TypeError: Cannot set property '0' of undefined
L=null
L[0]=1
//TypeError: Cannot set property '0' of null
Am I getting warmer? I would assume that if I performed some math on L[0] when isNaN(L)===true that the math is being done but not stored so the line isn't being ignored really..
If I understand what you want I would do something like this.
var op = [...],
opchoice = {
//these can return nothing, no operation, or a new value.
'true': function(val){ /*operation do if true*/ },
'false': function(val){ /*operation do if false*/ },
//add more operations here.
//keys must be strings, or transformed into strings with operation method.
operation: function(val){
//make the boolean a string key.
return this[''+(val == 'something')](val);
}
};
var endop = [];//need this to prevent infinite recursion(loop).
var val;
while(val = op.shift()){
//a queue operation.
endop.push(opchoice.operation(val));
}
I'm sure this is not exactly what you want, but it's close to fulfilling the want of not having a ton of conditions every where.
Your other option is on every line do this.
A = isNaN(A) ? A.concat(...) : A;
Personally I prefer the other method.
It looks like you repeat many of the operations. These operations should be functions so at least you do not redefine the same function over and over again (it is also an optimization to do so).
function get_min(x)
{
return +(MIN.apply(this, a[0])).toFixed(7);
}
function get_max(x)
{
return +(MAX.apply(this, a[0])).toFixed(7);
}
function get_average(x)
{
return (x[0].reduce(function(a, b) {return a + b})) / (x[0].length);
}
function get_mean(x)
{
return (MAX.apply(this, x[0]) - MIN.apply(this, x[0])) / 2;
}
if(C[0].length > 0)
{
L = L[1].concat(get_min(L));
C = C[1].concat(C[0][0]);
H = H[1].concat(get_max(H));
V = V[1].concat(get_average(V));
B = B[1].concat(get_mean(B));
A = A[1].concat(get_mean(A);
D = D[1].concat(get_average(D));
S = S[1].concat(get_average(S));
}
You could also define an object with prototype functions, but it is not clear whether it would be useful (outside of putting those functions in a namespace).
In regard to the idea/concept of having a test, what you've found is probably the best way in JavaScript.
op[0] && S = S[1].concat(get_average(S));
And if you want to apply multiple operators when op[0] is true, use parenthesis and commas:
op[3] && (V = V[1].concat(get_average(V)),
B = B[1].concat(get_mean(B)),
A = A[1].concat(get_mean(A));
op[0] && (D = D[1].concat(get_average(D)),
S = S[1].concat(get_average(S)));
However, this is not any clearer, to a programmer, than an if() block as shown in your question. (Actually, many programmers may have to read it 2 or 3 times before getting it.)
Yet, there is another solution which is to use another function layer. In that last example, you would do something like this:
function VBA()
{
V = V[1].concat(get_average(V));
B = B[1].concat(get_mean(B));
A = A[1].concat(get_mean(A));
}
function DS()
{
D = D[1].concat(get_average(D));
S = S[1].concat(get_average(S));
}
op = [DS,null,null,VBA,null,null,...];
for(key in op)
{
// optional: if(op[key].hasOwnProperty(key)) ... -- verify that we defined that key
if(op[key])
{
op[key](); // call function
}
}
So in other words you have an array of functions and can use a for() loop to go through the various items and if defined, call the function.
All of that will very much depend on the number of combinations you have. You mentioned 2,500 lines of code, but the number of permutations may be such that writing it one way or the other will possibly not reduce the total number of lines, but it will make it easier to maintain because many lines are moved to much smaller code snippet making the overall program easier to understand.
P.S. To make it easier to read and debug later, I strongly suggest you put more spaces everywhere, as shown above. If you want to save space, use a compressor (minimizer), Google or Yahoo! both have one that do a really good job. No need to write your code pre-compressed.

Javascript return position index of "matched" array within array

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.

Optimise a simple double for loop for IE

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];
});

Categories

Resources