Google Apps Script error when accessing nested array by index - for-loop

Google Apps Script is raising an error when I try to access the nested array with double indexing, it says: TypeError: Cannot read property "3" from undefined. (line 27, file "Code")
Here is the code:
var ss = SpreadsheetApp.openById("SpreadsheetID");
var sheetMAT = ss.getSheetByName("Sheet3");
var data = sheetMAT.getRange(3, 2, sheetMAT.getLastRow() - 1, 4).getValues();
var temporaryData = [];
var dataReadyLine = [];
function getReadyLine() {
var rawData = sheetMAT.getRange(3, 2, sheetMAT.getLastRow() - 1, 4).getValues();
Logger.log(rawData[0][3]);
for (var i=0; i<=rawData.length; i++) {
if (rawData[i][3] === "A Ready Line") {
temporaryData.push(data[i][1], data[i][0]);
dataReadyLine.push(temporaryData);
temporaryData = [];
}
}
return dataReadyLine;
};
The line 'Logger.log(rawData[0][3]);' successfully prints the value of the nested array item but when it comes to IF conditional it gives the error of undefined. Why is it giving this error? How can i make the FOR loop work?
Here is the print screen with the error when I try to run the code:
Print Screen

How about a following modification for the for loop.
From :
for (var i=0; i<=rawData.length; i++) {
To :
for (var i=0; i<=rawData.length - 1; i++) {
or
for (var i=0; i<rawData.length; i++) {
Index of Array is from 0 to Array.length - 1. So for (var i=0; i<=rawData.length; i++) { occurs an error at rawData.length.
As another expression, you can use
for (var i in rawData) {
In this case, you can also retrieve elements by rawData[i][3].

Related

Speed Up Find-and-Replace Google Apps Script Function for sheets

I've written a pretty simple script that successfully takes information from one sheet in a Google Spreadsheet, and replaces information in a column in another sheet in the same spreadsheet pending satisfaction of two criteria: the receiving row has the same "Customer ID" and "Product Type." I say "simple" because it's intuitive, but extremely computationally demanding (taking nearly 30 seconds to run!).
From what I've read online, it's the sequential read and write operations that are causing the slowdown. I'm assuming that if I sort the sheets in question on the two criteria and THEN do a function that writes over subsequent rows, I may be able to speed it up. I'm a little weak on algorithms, so I'm still scratching my head on how to do this elegantly.
Does anyone have any suggestions? Below is my original script, and I've already made sure that the spreadsheet collapses empty rows, so time isn't wasted iterating over nothing.
function replaceRawWithRepChanges(receivedSheet) {
var ss = SpreadsheetApp.openById(receivedSheet);
var repchanges = ss.getSheetByName('repchanges');
var rawSheet = ss.getSheetByName('Sheet1');
var rawTMtoReplace = rawSheet.getRange('P2:P');
var repCustID = repchanges.getRange('A1:A').getValues();
var repTM = repchanges.getRange('F1:F').getValues();
var repCategory = repchanges.getRange('G1:G').getValues();
var rawCustID = rawSheet.getRange('A2:A').getValues();
var rawTM = rawSheet.getRange('P2:P').getValues();
var rawCategory = rawSheet.getRange('U2:U').getValues();
var repInfo = [repCustID, repTM, repCategory];
var rawInfo = [rawCustID, rawTM, rawCategory];
for (var i=0; i < rawInfo[0].length; i++) {
for (var j=0; j < repInfo[0].length; j++) {
// var thisRawCust = rawInfo[0][i];
// var thisRepCust = repInfo[0][j];
if (rawInfo[0][i].toString() == repInfo[0][j].toString()) {
// var thisRawCategory = rawInfo[2][i];
// var thisRepCategory = repInfo[2][j];
if (rawInfo[2][i].toString() == repInfo[2][j].toString()) {
// var repvalue = repInfo[1][j];
rawInfo[1][i] = repInfo[1][j];
// var newRawValue = rawInfo[1][i];
}
}
}
}
return rawInfo[1];
}
Yes, you should sort the data (perhaps using the SORT command, which does work with multiple columns). Then, using two pointers, you only have to go down the columns once, rather than checking the entirety of repInfo for matches for every single row in rawInfo.
Once you've sorted the information, your loop might look like the following:
var i = 0;
var j = 0;
while (i < rawInfo[0].length && j < repInfo[0].length) {
if (rawInfo[0][i].toString() == repInfo[0][j].toString()) {
if (rawInfo[2][i].toString() == repInfo[2][j].toString()) {
rawInfo[1][i]=repInfo[1][j];
i++;
j++;
} else if (rawInfo[2][i].toString() < repInfo[2][j].toString()) {
i++;
} else {
j++;
}
} else if (rawInfo[0][i].toString() < repInfo[0][j].toString()) {
i++;
} else {
j++;
}
}

Kendogrid population

I'm new to all this kendo stuff i need help in populating kendogrid from a csv file.
The csv data is stored in an array of strings returned by a service.
Data looks like :
0: "Module,LogLevel,LogType,LoggedTime,LogMessage"
1: "00D02D5A4B66 ,CommServer ,Level3 ,Information ,03/16/2015 00:32:57:5716 ,[ISOMMessageHandler::Initialize]-[EventCount:20,ObjectRetryCount:6]"
2: "00D02D5A4B66 ,CommServer ,Level1 ,Information ,03/16/2015 00:32:57:5716 ,ISOMProtocolHandler::HandleConnectGeneric] - Before UpdatePanelTouched - CommServerID : 1, ConnectionMode : 2"
3: "00D02D5A4B66 ,CommServer ,Level4 ,Information ,03/16/2015 00:32:57:5716 ,[PanelDataConfigurationHandler : UpdatePanelConnectionStatus] : CommServerID 1, CommMode : 2"
i need to display 0th indexed data as title of the columns
and rest in cells of the column.
My advice is to make a wrapper method yourself and get it into JSON.
needed wrapper as told by Thomas.
here is my wrapper function
function csvJSON(lines) {
var result = [];
var headers = lines[0].split(",");
headers.unshift("MAC");
for (var i = 1; i < lines.length; i++) {
var obj = {};
var currentline = lines[i].split(",");
for (var j = 0; j < headers.length; j++) {
obj[headers[j]] = currentline[j];
}
result.push(obj);
}
return result;
}

As3 Position on top based on Y without lag?

I was creating a position script with my own made tag system to position all the objects in order of the y position they have. This script makes my game lag so I wanted to ask if there is a better way of doing this.
PS: This code is used every frame.
private function positionGameObjectToLayer():void
{
var objectOnScreen : Array = [];
var parentObj : Sprite;
var l : int;
l = gameObjects.length;
for (var i : int = 0; i < l; i++) {
/*checks if the object has the position tag*/
if (gameObjects[i].checkTag(Tags.POSITION_ON_Y_TAG)) {
objectOnScreen.push(gameObjects[i]); //if it does it goes into this array
}
}
objectOnScreen.sortOn("y", Array.NUMERIC); /* sorts the array on y position*/
l = objectOnScreen.length;
for (i = 0; i < l; i++) {
/*this sets the layer of the object in order of the array*/
parentObj = objectOnScreen[i].parent;
parentObj.setChildIndex(objectOnScreen[i],parentObj.numChildren - 1);
}
l = gameObjects.length;
for (i = 0; i < l; i++) {
//if it has the always on top tag
if (gameObjects[i].checkTag(Tags.POSITION_ON_TOP_TAG)) {
/*then this code will grab that object and place it over the other layers*/
parentObj = gameObjects[i].parent;
parentObj.setChildIndex(gameObjects[i], parentObj.numChildren - 1);
}
}
}

How to make sure that any new tag added to the XML document will be loaded to the browser

I am using the below code to loop through an XML document in order to get the "food" elements. I want to make sure that when an XML element is added it will be shown in the browser automaticaly as a list item.
var myNodeList = document.getElementsByTagName('food')
for (var i=0; i < myNodeList.length; i++) {
var output = '<ul>';
var food = request.responseXML.getElementsByTagName('food')[i];
I believe I was able to figure it out through the following approach:
var foodList = request.responseXML.getElementsByTagName('food')
for (var i=0; i < foodList.length; i++) {
var output = '<ul>';
var food = request.responseXML.getElementsByTagName('food')[i];

Need get all A tags in selection in editable iframe and add them attribute "class"

I have an editable <iframe> with the some HTML code in it. I need get all <a> tags in my range. I tried this code but it doesn't work:
var select = document.getElementById(iframe_id).contentWindow.getSelection();
var range = select.getRangeAt(0);
//HERE I WANT TO FIND ALL TAGS IN THIS RANGE AND IF IT "A" - ADD NEW ATTRIBUTE "CLASS". SOMETHING LIKE THIS
var parent = rng.commonAncestorContainer;
for(var i=0; i<parent.childNodes.length; i++)
{
if(parent.childNodes[i].tagName.toLowerCase() == "a")
parent.childNodes[i].setAttribute("class", "href_class");
}
You can use getElementsByTagName() to get all <a> tags of the range container and then check for each of them whether it actually belongs to the range using range.compareBoundaryPoints() (only parts of the container might be selected). Something like this:
var links = rng.commonAncestorContainer.getElementsByTagName("a");
for (var i = 0; i < links.length; i++)
{
var linkRange = document.createRange();
linkRange.selectNode(links[i]);
if (rng.compareBoundaryPoints(Range.START_TO_START, linkRange) <= 0 && rng.compareBoundaryPoints(Range.END_TO_END, linkRange) >= 0)
{
links[i].className = "href_class";
}
}
This should get you started in the right direction. This code does not do any null reference checks on the iframe, selection, range or list.
function addAnchorClass(targetFrameId) {
var targetIframe = document.getElementById(targetFrameId).contentWindow;
var selection = targetIframe.getSelection();
var range = selection.getRangeAt(0);
var alist = range.commonAncestorContainer.getElementsByTagName("a");
for (var i=0, item; item = alist[i]; i++) {
if (selection.containsNode(item, true) ) {
item.className += "PUT YOUR CSS CLASS NAME HERE";
}
}
}

Resources