I have a code that is supposed to read a google sheet and push these into an array but i keep encountering an error that proData.push is not a function What could I be missing in this code?
function getData() {
var values = SpreadsheetApp.getActive().getSheetByName("Projects").getRange("A1:O").getValues();
values.shift();
var proData = [];
values.forEach(function(value) {
var proData = {};
proData.project_state = value[0];
proData.project_name= value[4];
proData.project_code= value[5];
proData.end_date= value[2];
proData.push(proData);
})
Logger.log(JSON.stringify(proData));
return proData;
}
I will appreciate help in looking at this.
This line tells the whole story:
proData.push(proData);
You're trying to push an object onto itself? Clearly this is an indication that something is wrong. So let's look at where you define proData:
var proData = {};
That at least explains the error. proData is an object, not an array. And an object indeed doesn't have a function called push. You may have thought it was an array, because you defined an identically named array here:
var proData = [];
But for the line where you call .push, how is the system to know which variable you intend for what purpose? In a higher scope you have an array named proData, but in the current scope of this operation you obscured that with an object named proData. And in doing so made the array inaccessible within the scope of the function passed to .forEach.
To avoid confusing both the JavaScript engine and yourself in this matter, simply use different variable names. Re-naming the variable in the smaller scope has a lower impact, so that's a good candidate. (Though it's not always the best choice. If the variable in the higher scope is semantically not clear about what it contains then it should be re-named.)
Something like this:
values.forEach(function(value) {
var pd = {};
pd.project_state = value[0];
pd.project_name = value[4];
pd.project_code = value[5];
pd.end_date = value[2];
proData.push(pd);
});
Related
First of all, I'm aware there are many questions about closures in JavaScript, especially when it comes to loops. I've read through many of them, but I just can't seem to figure out how to fix my own particular problem. My main experience lies with C#, C++ and some ASM and it is taking some getting used to JavaScript.
I'm trying to populate a 3-dimensional array with new instances of a class (called Tile) in some for loops. All I want to do is pass along a reference to some other class (called Group) that gets instantiated in the first loop (and also added to another array). As you might have guessed, after the loops are done, every instance of the Tile class has a reference to the same Group object, namely the last one to be created.
Apparently instead of passing a reference to the Group object, a reference to some variable local to the function is passed along, which is updated in every iteration of the loop. My assumption is that solving this problem has something to do with closures as this appears to be the case with many similar problems I've come across while looking for a solution.
I've posted some trimmed down code that exposes the core of the problem on jsFiddle:
//GW2 namespace
(function( GW2, $, undefined ) {
//GW2Tile class
GW2.Tile = function(globalSettings, kineticGroup)
{
//Private vars
var tilegroup = kineticGroup;
// console.log(tilegroup.grrr); //Shows the correct value
var settings = globalSettings;
this.Test = function(){
console.log(tilegroup.grrr);
}
this.Test2 = function(group){
console.log(group.grrr);
}
} //Class
}( window.GW2 = window.GW2 || {}, jQuery ));
var zoomGroups = [];
var tiles = [];
var settings = {};
InitArrays();
tiles[0,0,0].Test(); //What I want to work, should give 0
tiles[0,0,0].Test2(zoomGroups[0]); //How I'd work around the issue
function InitArrays(){
var i, j, k, zoomMultiplier, tile;
for(i = 0; i <= 2; i++){
zoomGroups[i] = {};
zoomGroups[i].grrr = i;
tiles[i] = [];
zoomMultiplier = Math.pow(2, i);
for(j = 0; j < zoomMultiplier; j++){
tiles[i,j] = [];
for(k = 0; k < zoomMultiplier; k++){
tile = new GW2.Tile(settings, zoomGroups[i]);
tiles[i,j,k] = tile;
}
}
}
}
Up till now when working with JavaScript, I've generally fiddled with the code a bit to make it work, but I'm tired of using work-arounds that look messy as I know there should actually be some fairly simple solution. I'm just not fond of asking for help, but this is really doing my head in. Any help is very much appreciated.
Multidimensional arrays
The problem
The first issue with your code above is how you are attempting to create multidimensional arrays.
The syntax you are using is:
tiles[0,0,0]
However, the way JavaScript will interpret this is:
tiles[0]
Accessing a multidim array
If you wish to access a multidim array you have to use:
tiles[0][0][0]
And to create a multidim array you would need to do the following:
tiles = [];
tiles[0] = [];
tiles[0][0] = [];
tiles[0][0][0] = 'value';
or:
tiles = [[['value']]];
With respect to your code
In your code you should be using:
tiles[i][j][k] = tile;
But you should also make sure that each sub array actually exists before setting it's value, otherwise you'll get undefined or illegal offset errors.
You can do this by way of:
(typeof tiles[i] === 'undefined') && (tiles[i] = []);
(typeof tiles[i][j] === 'undefined') && (tiles[i][j] = []);
tiles[i][j][k] = tile;
Obviously the above can be optimised depending on how you are traversing your loops i.e. it would be best to make sure the tiles[i] level exists as an array before stepping in to the the [j] loop, and then not worry about checking it's existence again whilst stepping j.
Other options
Depending on what your dataset is, or at least what you hope to do with the tiles array it can be worth considering using an object instead:
/// set up
tiles = {};
/// assignment
tiles[i+','+j+','+k] = 'value';
However this method is likely to be slower, although I've been proved wrong a number of times by my assumptions and differing JavaScript interpreters. This would probably be were jsPerf would be your friend.
Optimisation
One benefit of using the tiles[i][j][k] approach is that it gives you the chance to optimise your references. For example, if you were about to process a number of actions at one level of your multidimensional array, you should do this:
/// set up
var ij = tiles[i][j];
/// use in loops or elsewhere
ij[k] = 'value'
This is only of benefit if you were to access the same level more than once however.
Since now, I've been accessing box2d bodies (to change or to get their values) with this for loop copied from the web:
for (var b = world.GetBodyList(); b != null; b = b.GetNext())
works fine but... Is there any other faster way to do so? Can't I just store a reference to the bodies in an array so then have always access to it?
Thanks!
I don't know which version of Box2DJs you're using but you can do something like this:
var myBodies = [];
var myBox = new b2BoxDef(); // or whatever the name of your body creation function is
myBodies.push(myBox);
console.log(myBodies[0]); // reference to your Box2D object
I've seen something similar to this code in the Google API JavaScript, I mean the r=Array part. Here is an example of what they have done:
var r = Array;
var t = new r('sdsd' , 'sdsd');
alert(t[0]);
Few questions about this:
Is it legal to write like this and won't cause any problems?
I can do something similar with other keywords like ´For´ loop or with the ´this´ keyword?
Can I have article about this JavaScript official keyword shortcuts etc..?
Thank you in advance.
That works because Array is an object. You can do that with any object. For example, the Date object:
var d = Date;
console.log((new d()).getTime()); //Prints time
You cannot do that for keywords such as for or while because they are language constructs that will be recognised by the interpreter.
You can do it with this:
document.getElementById("b").onclick = function() {
var x = this; //this holds a reference to the DOM element that was clicked
x.value = "Clicked!";
}
In fact, that can be very useful sometimes (to keep a reference to this so you can access it from an anonymous inner function for example). This also works because, to put it simply, this will be a reference to an object.
Yes
for - no. this - yes.
You can store references to any JavaScript object in a variable. String, Array, Object, etc. are JavaScript objects that are built-in to the language. for, if, while, etc. are are JavaScript statements, and cannot be stored or referenced any other way.
You can do it the other way around as well (and really mess yourself up in the process):
Array = 0;
var myArray = new Array("a", "b", "c"); // throws error
This is easily undone like this:
Array = [].constructor;
Edit: Being able to assign the value of this to a variable is essential when nesting functions that will execute in a different scope:
function Widget() {
var that = this;
this.IsThis = function() {
return isThis();
};
function isThis() {
return that == this;
}
}
new Widget().IsThis(); // false!
Maybe not the best example, but illustrates losing scope.
You cannot reassign the value of this:
function doSomething() {
this = 0; // throws error
}
I had the following question in a test today. But i had not see something like functionName.VariableName before. Not sure how that works.
Would be great if you can tell me the solution:
function Item(itemName)
{
var next_item_id = 1;
Item.item_name = itemName;
Item.item_id = next_item_id++;
}
var Item1 = Item('Desktop');
var Item2 = Item('Laptop');
var Item3 = Item('Monitor');
Anything wrong with the code above? if yes fix it. (The problem i would see is next_item_id is always 1, need to make it global?)
Modify the function so that the variable “next_item_id”, cannot be modified during run time.
My own question, how does the variable like Item.item_name work? I want to google it, but not sure what I should search for.
Thanks.
Your thinking is close in that next_item_id will always be 1, but it's generally not recommended to pollute the global namespace. Instead, wrap it in an anonymous function:
(function() {
var next_item_id = 1;
function Item(itemName)
{
//Use "this" to apply the property to the instance only
this.item_name = itemName;
this.item_id = next_item_id++;
}
var Item1 = new Item('Desktop');
var Item2 = new Item('Laptop');
var Item3 = new Item('Monitor');
})()
Also, as in Java, the general best practice in Javascript is to use camelCase rather than under_scores. Constructors are generally in UpperCamelCase. Examples:
Array //UpperCamelCase
Object.prototype.toString //toString is camelCase
This question is ambiguous, and, depending on the interpretation, there can be a number of possible answers. next_item_id is a "var" declared inside a function and naturally has an internal [[DontDelete]] and [[DontEnum]] flag. If it's a "var" and we're not using "this" as shown in my modified code, the variable is inherently not accessible outside of the function or its nested functions and therefore cannot be modified. You can use the non-standard const or you can create an object and use Object.defineProperty to create a setter that returns false assuming an ES5-compatible environment, etc.
Functions are objects in Javascript. All objects can have "properties."
Javascript functions are also objects and can have properties.
They behave like static fields in Java.
You're going about it all wrong. I'm assuming that you want to set internal variables.
If you want to create a 'constructor', do something like this:
function Item(itemName)
{
var next_item_id = 1;
this.item_name = itemName;
this.item_id = next_item_id++;
}
var Item1 = new Item('Desktop');
var Item2 = new Item('Laptop');
var Item3 = new Item('Monitor');
Both item_name and item_id will be publicly available. To make use next_item_id like you want, try this:
Item.next_item_id = 1;
Then in your constructor, do Item.next_item_id++;
Your final code should look something like this:
function Item(itemName)
{
this.item_name = itemName;
this.item_id = Item.next_item_id++;
}
Item.next_item_id = 1;
var Item1 = new Item('Desktop');
var Item2 = new Item('Laptop');
var Item3 = new Item('Monitor');
What this is doing is attaching a property to the object Item. You might be thinking, "Item is a function! How is this possible?!". Nearly everything in JavaScript is an object with mutable properties. The only things that aren't are the keywords null and undefined (AFAIK).
Item is still a function, but it also has a property next_item_id.
Also, using new will create a new instance. This is a similar concept as in Java and other programming languages. Just calling Item is like calling a function, and you'll get back whatever is returned from it (through an explicit return statement, otherwise undefined).
Answer for question 2 : Modify the function so that the variable “next_item_id”, cannot be modified during run time.
If I have interpreted your question correctly, you want to have Item.item_id as a constant that cannot be modified any where outside the constructor. I don't think we can have constants in JS.
Item1.item_id = someValue
Above line, used some where can change the item_id value of Item1.
Sorry if this has been answered already, but I could not find an appropriate answer on here.
I've started writing my javascript code in a modular style lately and I have a question regarding how module variable scope works.
The following code gives me a conflicting answer.
I have a module named Base that declares two strings and an array. It also has a function called fetchData that uses the jQuery getJSON shortcut to set these variables with server data. Unfortunately when I ask for Base's string1 or string2, I get undefined. I understand that this is probably due to the fact that I have it set their values two functions deep (inside the AJAX callback and inside fetchData) and the scope limits it from seeing Base.string1 and Base.string2.
However, when I look at Base.array1 from outside the module, it's set to the appropriate data I pulled from the server, even though it's set from the same scope as the strings.
Here's the code:
namespace.Base = (function(){
var string1, string2, array1 = [];
function fetchData(){
$.getJSON('backendScript.php', function(data){
string1 = data.string1;
string2 = data.string2;
arrayCount = data.arr.length;
for(var i = 0; i<arrayCount; i++){
array1[i] = data.arr[i];
}
})
}
return{
fetchData: fetchData,
string1: string1,
string2: string2,
array1: array1
}
})();
If I change
string1 = data.string1;
to
namespace.Base.string1 = data.string1;
it works like I want.
So my question is, why is array1 set correctly when it's set from the same scope as the strings?
Also, what is the remedy for setting module-level variables from within the module's functions without having to give a global path (e.g. namespace.Base.string1)?
The problem is that you actually have two different references, the variable string1 within the closure of the anonymous function that you invoke to create namespace.Base, and namespace.Base.string1, which is on the object returned from that anonymous function. Your assignment of the variable string1 to the object property string1 is a one-time set, not a live reference. Further modification of the variable string1 will not affect the object property. Here's what you want:
namespace.Base = (function() {
var my = {
string1: null,
string2: null,
array1: [],
fetchData: function () {
$.getJSON('backendScript.php', function(data){
my.string1 = data.string1;
my.string2 = data.string2;
var arrayCount = data.arr.length;
for (var i = 0; i < arrayCount; i++){
my.array1[i] = data.arr[i];
}
});
}
};
return my;
})();
Now the local, but public, members of namespace.Base are in the object my. You can create private variables using var within the anonymous function, or create more public properties by adding them to my.
I would be a good idea to get familiar with closures and how they work:
How do JavaScript closures work?
Your "scope" issue is not actually a scope issue. The issue is that arrays are pointers to their data, strings are not.
namespace.Base is set to the results (returned value) of the anonymous function. -- It is set to be an object containing a function ref (fetchData), two empty strings and an array.
If you later call the fetchData function, then it will change the contents of array1.
But it will also create two new strings (from data.string1 and data.string2). The old values of string1 and string2 (which are namespace.Base.string1 and namespace.Base.string2) are not changed. So they are left as empty strings (not what you want).
Example of this. Try it in Firebug--
s1 = "Hi";
s2 = s1; // s2 => "Hi"
s1 = "Bye"
alert(s2); // *** s2 is still "Hi", it was not changed!
// But arrays are different:
a1 = ["Hi"];
a2 = a1;
a1[0] = "Bye";
alert(a2[0]); // a2[0] is now "Bye"
Added: Asynch Timing error
Also, note that your code is wrong as written since you're not giving the caller any way to know when the Ajax call has completed:
namespace.Base.fetchData(); // starts the Ajax call via getJSON method
var a = namespace.Base.array1; // ERROR!! The value of namespace.Base.array1 is
// indeterminate since you don't know if the
// the Ajax request has completed yet or not!
You appear to be trying to convert the asynchronous Ajax call (which invokes a callback function once the answer has been received from the remote server) into a synchronous call which will not return until the results have been received.
This is a really bad idea. (If you want to know more, ask another question in SO.)