Create an array from JSON in Javascript - javascript

I've researched quite a bit on here and I can't seem to find something that will work for me. What I have is an application that I'm trying to have go out and return the next four bus arrival times for a bus stop. I am reaching out to an API that returns this data in a JSON file. The problem I am having is I can see my request go out via fiddler but I can't seem to get the data into an array. Below is the code that I'm dealing with right now. I'm trying to get the returned data into a table format which you can see I'm failing at.
Eventually I want to get a popup to appear when the user clicks on the Show me the next 4 bus arrival times but this was for testing purposes. I would love to have the users click on my button which calls this function and then something like a like table open with these values. If you can help with that within this code I would appreciate it as well.
JSON Data:
[{"ARRIVAL":"01:23P","ROUTE":"208","DIR":"E"},
{"ARRIVAL":"01:53P","ROUTE":"208","DIR":"E"},
{"ARRIVAL":"02:23P","ROUTE":"208","DIR":"E"},
{"ARRIVAL":"02:53P","ROUTE":"208","DIR":"E"}]
Code:
<script>
function getTimes(stopNumber) {
var busArrivalAPI = "http://blahblahblah/rtcTimes/" + stopNumber ";
$.getJSON(busArrivalAPI, function(busArrivals) {
var a = [];
for (var i = 0; i < busArrivals.length; i++) {
a[i] = [busArrivals[i].ROUTE, busArrivals[i].ARRIVAL, busArrivals[i].DIR];
document.getElementById("results").createElement("TR");
for (var b = 0; b < 3; b++) {
var x = document.createElement("TH");
var z = a[i][b];
var t = document.createTextNode(z);
x.appendChild(t);
document.getElementById('results').appendChild(x);
};
};
});
</script>
My DIV:
<div style="overflow-x:scroll; overflow-y:scroll;" data-dojo-type="dijit/layout/ContentPane" data-dojo-props="title:'Bus Arrival Times', selected:true">
<table id = 'results'>
<tr>
<th>Route</th>
<th>Arrival Time</th>
<th>Direction</th>
</tr>
</table>
</div>
UPDATE: Ok, I've use the makeTable idea provide below and it works when I program as seen below hard coding the json data. However, when trying to use the $.getJSON I'm having some cross domain issues now and don't know how I can get my $.getJSON request working. Any input on how to get the data from my getJSON request work be great.
function getTimes(stopNumber) {
// This is the API address I need to hit. Trying to figure out how to incorporate that and remove the function getJSON where I have the data hard coded.
//var busArrivalAPI = "http://-----/rtcTimes/"+ stopNumber + "?jsoncallback=?";
function makeTable(busArrivals) {
// This will remove old values so table will only load current Times
var results = document.getElementById("results");
var rowCount = results.rows.length;
for (var x=rowCount-1; x>0; x--) {
results.deleteRow(x);
}
// This will populate the result table with the correct bus routes/times/direction
busArrivals.forEach(function(busArrival) {
var tr = document.createElement('tr');
var route = document.createElement('td');
route.appendChild(document.createTextNode(busArrival.ROUTE));
var arrival = document.createElement('td');
arrival.appendChild(document.createTextNode(busArrival.ARRIVAL));
var direction = document.createElement('td');
direction.appendChild(document.createTextNode(busArrival.DIR));
tr.appendChild(route);
tr.appendChild(arrival);
tr.appendChild(direction);
document.getElementById('results').appendChild(tr);
});
}
function getJSON(callback) {
var data = [{"ARRIVAL":"05:23P","ROUTE":"201","DIR":"E"},
{"ARRIVAL":"05:54P","ROUTE":"202","DIR":"E"},
{"ARRIVAL":"06:33P","ROUTE":"203","DIR":"E"},
{"ARRIVAL":"07:11P","ROUTE":"204","DIR":"E"}];
callback(data);
}
getJSON(makeTable);
};

I think you could write a separate function to build the table, like this:
function makeTable(busArrivals) {
busArrivals.forEach(function(busArrival) {
var tr = document.createElement('tr');
var route = document.createElement('td');
route.appendChild(document.createTextNode(busArrival.ROUTE));
var arrival = document.createElement('td');
arrival.appendChild(document.createTextNode(busArrival.ARRIVAL));
var direction = document.createElement('td');
direction.appendChild(document.createTextNode(busArrival.DIR));
tr.appendChild(route);
tr.appendChild(arrival);
tr.appendChild(direction);
document.getElementById('results').appendChild(tr);
});
}
var busArrivalAPI = 'http://blahblahblah/rtcTimes/'+ stopNumber;
$.getJSON(busArrivalAPI, makeTable);
In each iteration of the forEach loop, you construct a tr element, insert the tds and finally put the whole thing inside the DOM.

You're creating a TR element, but never appending it to the table. Instead, you're appending the TH elements directly to the table, which is invalid.
function getTimes(stopNumber) {
var busArrivalAPI = "http://blahblahblah/rtcTimes/" + stopNumber;
$.getJSON(busArrivalAPI, function(busArrivals) {
var table = document.getElementById('results');
for (var i = 0; i < busArrivals.length; i++) {
var a = [busArrivals[i].ROUTE, busArrivals[i].ARRIVAL, busArrivals[i].DIR];
var row = document.createElement("TR");
for (var b = 0; b < 3; b++) {
var x = document.createElement("TH");
var z = a[b];
var t = document.createTextNode(z);
x.appendChild(t);
row.appendChild(x);
};
table.appendChild(row);
};
});
}
I'm not sure why you need the a array. If you just want to change get the object properties into an array so you can iterate over it, you can do that with a 1-dimensional array, you don't need to save all the other rows in a 2-dimensional array. I've changed a to a single array.

Related

Google Apps Script: Loop through a list

I have a deleteEachRow function that loops through a sheet and delete Rows that have a particular Column Value.
This works fine and was hoping to modify it in such a way that it loops through a multile sheets in the work-book and also delete rows based on multiple Column Values.
The deleteRow() script
//GLOBALS
var SS = SpreadsheetApp.openById("sheetID");
var SHEET = SS.getSheetByName("Sheet1");
var RANGE = SHEET.getDataRange();
var DELETE_VAL = "abc";
var COL_TO_SEARCH = 4; // The column to search for the DELETE_VAL (Zero is first)
function deleteEachRow(){
var rangeVals = RANGE.getValues();
//Reverse the 'for' loop.
for(var i = rangeVals.length-1; i >= 0; i--){
if(rangeVals[i][COL_TO_SEARCH] === DELETE_VAL){
SHEET.deleteRow(i+1);
};
};
};
What I have tried..
var SHEET = SS.getSheetByName(["Sheet1", "Sheet2"]);
var DELETE_VAL = ["abc","DEF"];
function deleteEachRow(){
var rangeVals = RANGE.getValues();
//Reverse the 'for' loop.
for(var i = rangeVals.length-1; i >= 0; i--){
for(var i=0; size = DELETE_VAL.length; i < size; i++){
if(rangeVals[i][COL_TO_SEARCH] === DELETE_VAL[i]){
for(var i=0; size = SHEET.length; i < size; i++){
SHEET[i].deleteRow(i+1);
};
};
};
};
};
Which completes executing from my logs, but does not actually work. I may have murdered some logic here, please pardon me, I am new to .gs/.js.
Thanks for your anticipated response.
Issue : You're passing array to getSheetByName, whereas as per documentation it accepts String only. i.e. Name of the single sheet you want to fetch.
https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet#getsheetbynamename
So you can modify your function to take sheet name as input and then delete rows in that sheet. Then call your function with desired sheet names. Something like this:
var spreadSheet = SpreadsheetApp.openById("sheetID");
var DELETE_VAL = "abc";
var COL_TO_SEARCH = 4; // The column to search for the DELETE_VAL (Zero is first)
function deleteEachRow(sheetName){
var SHEET = spreadSheet.getSheetByName(sheetName);
var RANGE = SHEET.getDataRange();
var rangeVals = RANGE.getValues();
// existing logic
};
// Invoke deleteEachRow() for each sheet you want to delete the rows
["Sheet1", "Sheet2"].forEach((sheetName) => deleteEachRow(sheetName));
Umair is right, there was a simply error in the first line. But I'd want to add that the sheet.deleteRow(row) is not the best practice in case if there are many rows to delete. This command is quite time consuming.
If you have more than dozen rows to delete it's better to grab all data from a sheet (or range) var data = range.getValues(), clear the sheet (or the range), to process the array inside the script and refill the sheet back with new data new_range.setValues(array). It will work much faster.

How do I make less calls to sheet.getRange and reduce the execution time in a double loop

I want to reduce the calls to sheet.getRange in the following function, because the execution times out.
I've tried re-arranging the loops and parsing the data as a double array, however the size of my spreadsheet changes every day and I need to be able to reference the columns by name only.
function runDuplicateRemover() {
var sheet= SpreadsheetApp.getActive().getSheetByName('Sheet 1');
var rangeData = sheet.getDataRange();
var lastRow = rangeData.getLastRow();
var Cdata = sheet.getDataRange().getValues();
// here I am accessing the column which is used to find duplicates
var colCRM = Cdata[0].indexOf("CRM ID")+1;
var arrayOfDuplicates = [];
for(i=1; i<lastRow; i++){
var cellToCompare = sheet.getRange(i+1,colCRM);
// I am just changing all the colors to see the execution
cellToCompare.setBackground("#88b4fc");
var crmToCompare =cellToCompare.getValue();
//checks to see that this value is not already contained in the rows to delete
if (!cellToCompare.isBlank() && (arrayOfDuplicates.indexOf(i)+1)==0 ){
arrayOfDuplicates.push(i);
cellToCompare.setBackground("#f9d9f9");
for (j = i+1; j<lastRow; j++) {
var cellCurrent = sheet.getRange(j+1,colCRM);
cellCurrent.setBackground("#f2fc88");
var crmCurrent = cellCurrent.getValue();
if (crmToCompare == crmCurrent) {
arrayOfDuplicates.push(j);
cellCurrent.setBackground("#fc92f1");
}
}
//pops last value since that's the only one I want to keep
sheet.getRange(arrayOfDuplicates.pop()+1,colCRM).setBackground("#dbf7d4");
}
}
for (t = arrayOfDuplicates.length-1; t>=0; t--) {
sheet.deleteRow(arrayOfDuplicates[t]+1);
}
}
I'd like to reduce the calls to the sheet.getRange, however I don't know how to delete the rows and then return the data back to the sheet without messing all the column order up.

Google apps script select specific rows with specific columns

I am trying to figure out how to pull only specific rows and columns using Google apps script. The =QUERY is not an option to use. Need to keep all of the logic in the script - my actual data set is quite large. To illustrate what I'm trying to solve. I have a little table of test data. TestData From which I only want columns 2,3,5 (zero based index) and only the rows with "fur".
function testFour(sheetID, fromTabName, toTabName) {
var sourceTab = SpreadsheetApp.openById(sheetID).getSheetByName(fromTabName);
var values = sourceTab.getDataRange().getValues();
var columns = [2,3,5]; //only want these columns
var output = new Array();
for (var i = 0; i < values.length; i++) {
for (var j = 0; j < values[0].length; j++) {
if (values[i][4] == "fur") { // only want these rows
if (j in columns) {
output.push(values[i][j]);
}
}
}
}
var targetTab = SpreadsheetApp.openById(sheetID).getSheetByName(toTabName);
targetTab.getRange(1, 1, output.length, columns.length).setValues(output);
}
This code bit works but it is not very maintainable if there are many columns:
for(n=0; n < values.length; ++n) {
var columns = [2,3,5];
if (values[n][4] === 'fur') {
output.push( [values[n][2], values[n][3], values[n][5]]);
}
When I run the testFour function I get an error about "Cannot covert Array to Object[][] on this line of the code:
targetTab.getRange(1, 1, output.length, columns.length).setValues(output);
Appreciate any help. I was really surprised to not have found any examples of this. Every solution was using the =QUERY on the sheet.
Based on Cooper's suggestion above this was how I was able to get this to work:
function testFourNew(sheetID, fromTabName, toTabName) {
var sourceTab = SpreadsheetApp.openById(sheetID).getSheetByName(fromTabName);
var targetTab = SpreadsheetApp.openById(sheetID).getSheetByName(toTabName);
var srcrg = sourceTab.getDataRange();
var srcvA = srcrg.getValues();
var desvA=[];
// var columns = [2,3,5]; //only want these columns
var columns = String("2,3,5").split(','); //only want these columns
var tstVal = "fur";
var tstCol = 4;
for (var i=0;i<srcvA.length;i++) {
var tA=[];
if (srcvA[i][tstCol] ==tstVal) {
for (var j=0;j<columns.length;j++) {
//Logger.log(srcvA[i][columns[j]]);
tA.push(srcvA[i][columns[j]]);
}
desvA.push(tA);
}
}
targetTab.getRange(1, 1, desvA.length, desvA[0].length).setValues(desvA);
}
Thank you Cooper your direction and suggestions!
EDIT:
Don't need [] around output if using this line to push to output -
output.push( [values[n][2], values[n][3], values[n][5]]);
To set values by row, data has to be in this format -
[ row,
row ]
or,
[ [1,2,3],
[4,5,6] ]
Cannot covert Array to Object[][] error is shown when range and value format do not match.
Try this line -
targetTab.getRange(1, 1, output.length, columns.length).setValues([output]);
Report Builder
I'm guessing that you want to use this function to build different reports from the same dataset. So I might try something like this:
The Function:
function testFour(t4Obj) {
var ss=SpreadsheetApp.openById(t4Obj.ssId);
var srcsh=ss.getSheetByName(t4Obj.srcShName);
var dessh=ss.getSheetByName(t4Obj.desShName);
var colA=String(t4Obj.zbcols).split('~~~');
var tstCol=Number(t4Obj.zbtstCol);
var tstVal=t4Obj.tstVal;
var srcrg=srchsh.getDataRange();
var srcvA=srcrg.getValues();
var desvA=[];
for (var i=0;i<srcvA.length;i++) {
var tA=[];
if (srcVa[i][tstCol]==tstVal) {
for (var j=0;j<colA.length;j++) {
tA.push(colA[j]);
}
desvA.push(tA);
}
}
dessh.getRange(1, 1, desvA.length, desvA[0].length).setValues(desvA);
}
The t4Obj Data Table:
Of course you would have to build yourself a function that loads tthe t4Obj from the above table. From looking at your code, I'd guess that will be no problem for you.
And please note I have not tested any of this code so it is extremely unlikely to work the first time out of the box. You can sharpen your debugging skills on it.
Let's try making a array for a square range where the number incremented in by one in the same column position down each row. The array would look like this:
var A=[[1,6,11,16,21],[2,7,12,17,22],[3,8,13,18,23],[4,9,14,19,24][5,10,15,20,25]];
Make an Array by hand
function makeArray() {
var A=[[1,6,11,16,21],[2,7,12,17,22],[3,8,13,18,23],[4,9,14,19,24],[5,10,15,20,25]];
var ss=SpreadsheetApp.getActive();
var sh=ss.getActiveSheet();
var rg=sh.getRange(5,5,5,5);//could be this way
//var rg=sh.getRange(5,5,A.length,A[0].length);//or in most programs this way
rg.setValues(A);
}

Update data from a Spreadsheet - Apps Script

I'm doing a data transfer of several spreadsheets to a single one, what I do is transfer the last data of certain columns to the master spreadsheet and also insert them in the last available row of certain columns, for now, I insert all the data but I would like to to know how I can have it examine the master spreadsheet so that if those data already exist, it does not delete them but update them. The script that I have is the following ...
function Gas10(){
var ss1 = SpreadsheetApp.openById("ID");
var ssh1 = ss1.getSheetByName("Sheet 1");
var lastRow1 = ssh1.getLastRow();
var gtRange1 = ssh1.getRange("C"+(lastRow1)+":K"+(lastRow1)).getValues();
var gtRange2= ssh1.getRange("A" + (lastRow1)).getValue();
var ss2 = SpreadsheetApp.getActiveSpreadsheet();
var ssh2 = ss.getSheetByName("Sheet 2");
var lastRow2 = ssh2.getLastRow() + 1;
var setRange1 = ssh2.getRange(lastRow2, 4, gtRange1.length, gtRange1[0].length).setValues(gtRange1);
var setRange2 = ssh2.getRange(lastRow2, 3).setValue(gtRange2);
}
I need to know how I can do it when I insert a piece of information (I already do that), but update it if it already exists. This is the example that I created so that it can be better understood, in this example I have two sheets of which from sheet 1 I pass data to sheet 2 and what I'm looking for is that sheet 2 updates all the data that are equal to (Name, Num, Proyect). I hope that now I understand better what I'm looking for.
Basically what you have to do is
get the new Line you want to add to the destination spreadsheet
get all the required datas of the destination spreadsheet
Check if the new Line datas have the same datas than in the destination data array
If so change ID value
paste changed datas in the destination spreadsheet
based on this spreadsheet The code should look something like this
function Gas10(){
var ss1 = SpreadsheetApp.getActiveSpreadsheet();
var ssh1 = ss1.getSheetByName("Sheet 1");
var ssh2 = ss1.getSheetByName("Sheet 2");
var lastRow1 = ssh1.getLastRow();
var lastCol1 = ssh1.getLastColumn();
var newLine = ssh1.getRange(lastRow1, 2, 1, lastCol1 - 1 ).getValues();
var destDatas = ssh2.getDataRange().getValues();
for (var i = 1; i < destDatas.length; i++)
{
if (newLine[0][0] == destDatas[i][0]
&& newLine[0][1] == destDatas[i][1]
&& newLine[0][2] == destDatas[i][2])
{
destDatas[i][3] = newLine[0][3];
}
}
// add newLine to destDatas
destDatas.splice(destDatas.length, 0, newLine[0]);
var lastColumn = ssh2.getLastColumn();
var lastRow2 = ssh2.getLastRow() + 1;
ssh2.getRange(1, 1, destDatas.length, lastColumn).setValues(destDatas);
ssh1.deleteRow(lastRow1);
}
Here's an example I played around with:
It looks at the slave sheet for any data. When it finds data it puts the row and col and value into an obj which is then added to an array. When it finishes it calls the updMaster which then looks for data in those same cells (assuming that the cells are in the same place if those cells are blank then it adds data and I also changed the background to lightblue to show me where it updated the cells.
You could run the getSlaveData() for different sheets if you wish.
function getSlaveData(){
var ss=SpreadsheetApp.getActive();
var ssh=ss.getSheetByName('Sheet2');
var sA=[];
var srg=ssh.getDataRange();
var svA=srg.getValues();
for(var i=0;i<svA.length;i++){
for(var j=0;j<svA[i].length;j++){
//if(svA[i][j]){
if(!ssh.getRange(i+1,j+1).isBlank()){//optional way to look for values
var sObj={};
sObj['row']=i + 1;
sObj['col']=j + 1;
sObj['value']=svA[i][j];
sA.push(sObj);
}
}
}
updMaster(sA);
}
function updMaster(sA){
var ss=SpreadsheetApp.getActive();
var msh=ss.getSheetByName('Sheet1');
for(var i=0;i<sA.length;i++){
if(msh.getRange(sA[i].row,sA[i].col).isBlank()){
msh.getRange(sA[i].row,sA[i].col).setValue(sA[i].value);
msh.getRange(sA[i].row,sA[i].col).setBackground('lightblue');
}
}
}

Data copying with Google Apps Script

I am trying to write a script in Google Apps Script that takes cell information from one sheet and copies it to another sheet, both for just grabbing certain columns to display on the second sheet and also a condition based on the values inside cells in a certain column. Here is what I have so far:
function onMyEdit() {
var myMaster = SpreadsheetApp.openById("xxxxx");
var masterSheet = myMaster.setActiveSheet(myMaster.getSheets()[0]);
var myNames = SpreadsheetApp.openById("xxxxx");
var namesSheet = myNames.setActiveSheet(myNames.getSheets()[0]);
var row1 = masterSheet.getRange(1, 1, masterSheet.getLastRow(), 1);
var rowV = row1.getValues();
var firstArray = masterSheet.getDataRange().getValues();
var dataList = [];
for (var i = 1; i < rowV.length; i++) {
dataList.push(firstArray[i][0]);
dataList.push(firstArray[i][1]);
dataList.push(firstArray[i][2]);
dataList.push(firstArray[i][3]);
}
for (var j = 0; j < rowV.length - 1; j++) {
namesSheet.getRange(2, j + 1, 1, 1).setValue(dataList[j]);
}
}
So as of now it only works on one row, starting from the second row (to allow for column headers). And I suppose when I want to grab rows conditionally based on cell data, I will use an 'if' statement for the condition inside the 'for' loop, but I want the data to copy to the next available row in both sheets. I suppose I'd use something like:
' getLastRow + 1 '
or something like that. I need this code to be as efficient as possible because of the amount of data and its purpose. I am pretty new to programming so please explain in detail, and thanks again.
I'm not sure I understood exactly what you wanted to do but -from what I understood- this code snippet should give you a better way to start with...
(I added a few comments to explain in the code itself)
function onMyEdit() {
var myMaster = SpreadsheetApp.openById("MasterSheet ID");
var masterSheet = myMaster.getSheets()[0]; // get 1rst sheet
var myNames = SpreadsheetApp.openById("NamesSheet ID");
var namesSheet = myNames.getSheets()[0]; // get 1rst sheet
var firstArray = masterSheet.getDataRange().getValues();
var dataList = [];
for ( r = 1; r < firstArray.length; r++) { // iterate the first col of masterSheet
if(firstArray[r][0]=='some condition'){ // if value in the first column == 'some condition get the second column cell in the new array (here you could change what you want to get)
dataList.push([firstArray[r][1]])
}
}
Logger.log(dataList)
if(dataList.length>0){
namesSheet.getRange(1,namesSheet.getLastColumn()+1,dataList.length,1).setValues(dataList);//copy data in a column after last col
}
}

Categories

Resources