Google script, empty array from fetched API - arrays

I'm trying to fetch my ETH balance and transactions from etherscan.io website and I'm trying to use the same code I used before for another website. But it seems returning me an empty array, and also the error "The coordinates or dimensions of the range are invalid."
This is the code:
function getTx() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Data");
var url = 'http://api.etherscan.io/api?module=account&action=txlist&address=0x49E81AA0cFE7eeA9738430212DC6677acF2f01a1&sort=asc';
var json = UrlFetchApp.fetch(url).getContentText();
var data = JSON.parse(json);
var rows = [],
array;
for (i = 0; i < data.length; i++) {
array = data[i];
rows.push([array.timeStamp, array.from, array.to, array.value]);
}
Logger.log(rows)
askRange = sheet.getRange(3, 1, rows.length, 3);
askRange.setValues(rows);
}
The logged "rows" is empty, what am I doing wrong?
Thank you

How about a following modification?
Modification points :
There is data you want at data.result.
Number of columns of data is 4.
Modified script :
function getTx() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Data");
var url = 'http://api.etherscan.io/api?module=account&action=txlist&address=0x49E81AA0cFE7eeA9738430212DC6677acF2f01a1&sort=asc';
var json = UrlFetchApp.fetch(url).getContentText();
var data = JSON.parse(json);
var rows = [],
array;
for (i = 0; i < data.result.length; i++) { // Modified
array = data.result[i];
rows.push([array.timeStamp, array.from, array.to, array.value]);
}
Logger.log(rows)
askRange = sheet.getRange(3, 1, rows.length, 4); // Modified
askRange.setValues(rows);
}
If I misunderstand your question, I'm sorry.

Related

How to append array data to spreadsheet rows using Google Script?

I have created a script to retrieve data from REST API. I can view all the array data in logger. How do I add all those data into rows. This is my current function:
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var mainSheet = ss.getSheetByName("test")
mainSheet.getRange('A1:A3').clear();
var apiKey = 'test';
var URL_STRING = "test";
var url = URL_STRING + "?ApiKey=" + apiKey;
var response = UrlFetchApp.fetch(url);
var json = response.getContentText();
var data = JSON.parse(json);
var arr = [];
//Logger.log(data.output.o1);
for (var i=0;i<data.output.o1.length;i++){
x=(data.output.o1[i].company_name);
arr.push(x);
Logger.log(arr);
}
}
This is the sample output for arr:
This is my expected output:
I believe your goal as follows.
You want to put the values of arr from row 2 of the column "A" in the sheet "test".
In this case, how about the following modification?
From:
for (var i=0;i<data.output.o1.length;i++){
x=(data.output.o1[i].company_name);
arr.push(x);
Logger.log(arr);
}
To:
for (var i = 0; i < data.output.o1.length; i++) {
x = (data.output.o1[i].company_name);
arr.push([x]); // Modified
Logger.log(arr);
}
mainSheet.getRange(2, 1, arr.length).setValues(arr); // Added
If you want to append the values to the sheet, please modify mainSheet.getRange(2, 1, arr.length).setValues(arr); as follows.
mainSheet.getRange(mainSheet.getLastRow() + 1, 1, arr.length).setValues(arr);
References:
getLastRow()
getRange(row, column, numRows)
setValues(values)

Need a custom function to repeat for each row shown and minimize API calls

EDIT: I need help combining functions into 1 and adding a trigger.
In my spreadsheet I have rows 4-100 for customer service calls that are filtered from a "ServiceData" worksheet by either choosing a "Service Month" or "Service Day" (ie. "7/11" shows only 5 rows where "July" would show 65 rows) . Each row item has corresponding Place IDs for origin/destination in column K and L with an order # (as in 1st, 2nd, 3rd... service call of the day) in column J .
TravelTime spreadsheet
I'm using the following custom function travelTime() in cells M4:M100 to calculate driving duration and distance between 2 place IDs:
function travelTime(origin,destination) {
var API_KEY = PropertiesService.getScriptProperties().getProperty('keyMaps');
var baseUrl = "https://maps.googleapis.com/maps/api/distancematrix/json? units=imperial&origins=";
var queryUrl = baseUrl + "place_id:" + origin + "&destinations=" +
"place_id:" + destination + "&mode=driving" + "&key=" + API_KEY;
var response = UrlFetchApp.fetch(queryUrl);
var json = response.getContentText();
var time = JSON.parse(json);
return [[ time.rows[0].elements[0].duration.text,
time.rows[0].elements[0].distance.text ]] ;
A major issue is that many unnecessary service calls are being made to the API when I'm making edits to the "ServiceData" spreadsheet (ie. service date changes when a particular day is over-scheduled) and not needing the travel time updated until I'm done working through a schedule .
After researching quite a bit there seems to be several options I could be using; caching, looping, arrays, and putting everything into a script then attach to a button to only run when ready. Considering I'm a newbie, putting all these options together are definitely beyond my skill level and could really use some help.
EDIT with new functions:
So after more researching I have been able to put together the following functions that when each are run independently work great. Now the problem I'm having is putting these all together in particular adjusting the original travelTime()into newTravelTime(). I have made an attempt towards the right direction below but can't figure out how to get the API call in there .
function newTravelTime() {//<--**having issues how to write this function
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sourceSheet = ss.getSheetByName("MonthlySA");
var sourceR = sourceSheet.getRange(4, 11, sourceSheet.getLastRow()-3, 4);
var sourceV = sourceR.getValues();
var array = [];
for (var i = 0; i < sourceV.length; i++) {
if (sourceV[i][2] == "") {
var origin = sourceV[i][0];//ori place IDs for API query
var destination = sourceV[i][1];//des place IDs API api query
}
array.push([sourceV[i][2]]);
}
sourceSheet.getRange(4, 13, array.length, 1).setValues(array);
I'd like to create a final getTravelTime() with all the functions and add an OnEdit trigger when either "Service Month" or "Service Day" changes in cells B1 or B2 to run them. If there is any advice with my functions themselves I would really appreciate some help, I am very new with this and trying.
///checks if origin/destination are already in the cacheSheet then return travel time to sourceSheet
function getCachedTravelTime() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sourceSheet = ss.getSheetByName("MonthlySA");
var sourceR = sourceSheet.getRange(4, 11, sourceSheet.getLastRow()-3, 4);
var sourceV = sourceR.getValues();
var cacheSheet = ss.getSheetByName("TravelTimeCache");
var cacheR = cacheSheet.getRange(2, 1, cacheSheet.getLastRow()-1, 4);
var cacheV = cacheR.getValues();
var array = [];
for (var i = 0; i < sourceV.length; i++) {
for (var j = 0; j < cacheV.length; j++) {
//if origin/destination columns from sourceSheet match columns on cacheSheet
if (sourceV[i][0]+sourceV[i][1] == cacheV[j][0]+cacheV[j][1]) {
sourceV[i][2] = cacheV[j][2]; //column with travel duration
sourceV[i][3] = cacheV[j][3]; //column with travel distance
}
}
array.push([sourceV[i][2], sourceV[i][3]]);
}
sourceSheet.getRange(4, 13, array.length, 2).setValues(array);
}
///if origin or destination are blank, label as 'missing value'
function missingOD() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sourceSheet = ss.getSheetByName("MonthlySA");
var sourceID = sourceSheet.getRange(4, 3, sourceSheet.getLastRow()-3, 12);
var sourceV = sourceID.getValues();
var array = [];
for (var i = 0; i < sourceV.length; i++) {
// if ID has a value
if (sourceV[i][0] != "") {
// if origin or destination is blank
if (sourceV[i][8] == "" || sourceV[i][9] == "") {
sourceV[i][10] = 'missing value';
}
}
array.push([sourceV[i][10]]);
}
sourceSheet.getRange(4, 13, array.length, 1).setValues(array);
}
///if cache not found - get the new travelTime for that origin/destination on sourceSheet...
function newTravelTime() {//<--
}
///...and store the new travelTime() in cacheSheet
function storeTravelTime() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sourceSheet = ss.getSheetByName("MonthlySA");
var sourceR = sourceSheet.getRange(4, 11, sourceSheet.getLastRow()-3, 4);
var sourceV = sourceR.getValues();
var cacheSheet = ss.getSheetByName("TravelTimeCache");
var cacheR = cacheSheet.getRange(2, 1, cacheSheet.getLastRow()-1, 4);
var cacheV = cacheR.getValues();
var array = [];
for (var i = 0; i < sourceV.length; i++) {
var duplicate = false;
for (var j = 0; j < cacheV.length; j++) {
if (sourceV[i][0]+sourceV[i][1] == cacheV[j][0]+cacheV[j][1]) {
duplicate = true;
}
}
if(!duplicate){ //if origin/destination columns from sourceSheet are NOT matched on cacheSheet
array.push([sourceV[i][0], sourceV[i][1], sourceV[i][2], sourceV[i][3]]);//columns with new data
}
}
//add new data to last row of cacheSheet
cacheSheet.getRange(cacheSheet.getLastRow()+1, 1, array.length, 4).setValues(array);
}
One of the easiest solution thats coming to mind is Caching. Instead of making API calls everytime check if we have already made that call previously.
Something like this
function getTravelTime(origin, destination) {
var travelTime = getTravelTimeFromPreviousCall(origin, destination);
if (travelTime != null) {
return travelTime;
} else {
var travelTime = fetchTravelTime(origin, destination);
storeTravelTime(origin, destination, travelTime);
return travelTime;
}
}
function fetchTravelTime(origin, destination) {
var API_KEY = PropertiesService.getScriptProperties().getProperty('keyMaps');
var baseUrl = "https://maps.googleapis.com/maps/api/distancematrix/json? units=imperial&origins=";
var queryUrl = baseUrl + "place_id:" + origin + "&destinations=" + "place_id:" + destination + "&mode=driving" + "&key=" + API_KEY;
var response = UrlFetchApp.fetch(queryUrl);
var json = response.getContentText();
var time = JSON.parse(json);
return time.rows[0].elements[0].duration.text;
}
For this we can define our cache something like :
A sheet with column -
origin
destination
travel time
And we need to define following functions:
getTravelTimeFromPreviousCall(origin, destination) : In this we need to check cache and return travel time for that origin & destination, if not found then return null
storeTravelTime(origin, destination, time) : This will only store travel time for future use in cache sheet
You can try something like this :
function getTravelTimeFromPreviousCall(origin, destination) {
var cacheSheet = SpreadsheetApp.openById(SPREADSHEET_ID).getSheetByName(CACHE_SHEET_NAME);
var cacheData = sheet.getDataRange().getValues();
for (var i=0; i<cacheData.length; i++) {
if (cacheData[i][ORIGIN_COLUMN_INDEX]==origin && cacheData[i][DESTINATION_COLUMN_INDEX]==destination) {
return cacheData[i][TRAVEL_TIME_COLUMN_INDEX];
}
}
return null;
}
function storeTravelTime(origin, destination, travelTime) {
var cacheSheet = SpreadsheetApp.openById(SPREADSHEET_ID).getSheetByName(CACHE_SHEET_NAME);
sheet.appendRow([origin, destination, travelTime]);
}
Please fix loop variable, array indexes & constants, as per your sheet.

Filter one array using a 2nd array, and display the results on a spreadsheet

----- BACKGROUND ----- In Google Sheets/Apps Script, I'm trying to move some transaction data from one sheet and organize it on a different sheet with subtotal rows for each customer account (called "Units") with a grand total at the bottom.
I have the transactions organized in an array of objects as key:value pairs ("transactions", the keys are headers, some values are strings, others are numbers), and I have the units in a 2nd array called "unitList". I want to iterate through each element in the unitList array, and pull each transaction with a matching unit onto my "targetSheet", then append a subtotal row for each unit.
----- UPDATE 10/7/2018 3:49PM EST -----
Thanks to everyone for your input - I took your advice and ditched the library I was using and instead found better getRowsData and appendRowsData functions which I put directly in my code project. This fixed the array filter problem (verified by logging filterResults), BUT, now when I call appendRowsData(), I get this error:
The coordinates or dimensions of the range are invalid. (line 73, file "Display Transactions")
Line 73 is the line below, in the appendRowsData function. Any help on how to fix this would be greatly appreciated.
var destinationRange = sheet.getRange(firstDataRowIndex, 1, objects.length, 9);
Here's my project in it's entirety thus far:
function displayTransactions() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
// Label each sheet
var dashboard = ss.getSheetByName('Dashboard');
var unitsSheet = ss.getSheetByName('Unit Ref Table');
var transactionsSheet = ss.getSheetByName('Transactions Ref Sheet');
var targetSheet = ss.getSheetByName('Target Sheet');
// Returns true if the cell where cellData was read from is empty.
// Arguments:
// - cellData: string
function isCellEmpty(cellData) {
return typeof(cellData) == "string" && cellData == "";
}
// Define function that converts arrays into JSON
// For every row of data in data, generates an object that contains the data.
// Names of object fields are defined in keys.
// Arguments:
// - data: JavaScript 2d array
// - keys: Array of Strings that define the property names for the objects to create
function getObjects(data, keys) {
var objects = [];
for (var i = 0; i < data.length; ++i) {
var object = {};
var hasData = false;
for (var j = 0; j < data[i].length; ++j) {
var cellData = data[i][j];
if (isCellEmpty(cellData)) {
continue;
}
object[keys[j]] = cellData;
hasData = true;
}
if (hasData) {
objects.push(object);
}
}
return objects;
}
// Define function that pulls spreadsheet data into arrays, then converts to JSON using getObjects function
function getRowsData(sheet) {
var headersRange = sheet.getRange(1, 1, 1, sheet.getLastColumn());
var headers = headersRange.getValues()[0];
var dataRange = sheet.getRange(sheet.getFrozenRows()+1, 1, sheet.getLastRow(), sheet.getLastColumn());
return getObjects(dataRange.getValues(), headers);
}
// Define function appendRowsData that uses getLastRow to fill in one row of data per object defined in the objects array.
// For every Column, it checks if data objects define a value for it.
// Arguments:
// - sheet: the sheet object where the data will be written
// - objects: an array of objects, each of which contains data for a row
function appendRowsData(sheet, objects) {
var headersRange = sheet.getRange(7, 1, 1, 9);
var firstDataRowIndex = sheet.getLastRow() + 1;
var headers = headersRange.getValues()[0];
var data = [];
for (var i = 0; i < objects.length; ++i) {
var values = []
for (j = 0; j < headers.length; ++j) {
var header = headers[j];
values.push(header.length > 0 && objects[i][header] ? objects[i][header] : "");
}
data.push(values);
}
var destinationRange = sheet.getRange(firstDataRowIndex, 1, objects.length, 9);
destinationRange.setValues(data);
}
// Call getRowsData on transactions sheet
var transactions = getRowsData(transactionsSheet);
// Get array of units
var unitList = unitsSheet.getRange("B2:B").getValues();
// Iterate through the unitList and pull all transactions with matching unit into the target sheet
for (var i=0; i < unitList.length; i++) {
var subTotal = 0;
var grandTotal = 0;
var filterResults = transactions.filter(function(x) {
return x['Unit'] == unitList[i];
})
Logger.log(filterResults); // This brings the correct results!
// Display matching transactions
appendRowsData(targetSheet, filterResults);
// Grand total at the bottom when i=units.length
}
}

App script/maker and Big Query

I having an google app (app maker) where I script the following code:
function runQuery() {
var projectId = 'projekte-123425512';
var request = {
query: 'SELECT title FROM [bigquery-public-data:samples.wikipedia] where title contains "olimpic" LIMIT 100'
};
var queryResults = BigQuery.Jobs.query(request, projectId);
var jobId = queryResults.jobReference.jobId;
var names = queryResults.schema.fields.map(function(field){ return field.name; });
return queryResults.rows.map(function(row) {
var obj = {};
for( var i = 0, len = names.length; i < len; ++i ) {
obj[names[i]] = row.f[i].v;
}
return obj;
});
}
So I basically try to get some data out of Big Query and store it in an array. Later I want it as a calculated model and a data source in my app:
I already tried it and get result in my logger:
It works! But only in app script debugger, then I want to test the whole app I get the following error:
The function queryRecords must return an array of records, but the array contained an element that was not a record. Error: The function queryRecords must return an array of records, but the array contained an element that was not a record.
EDIT
I updated my code
function runQuery() {
var projectId = 'nifty-stage-155512';
var request = {
query: 'SELECT title FROM [bigquery-public-data:samples.wikipedia] where title contains "olimpic" LIMIT 100'
};
var queryResults = BigQuery.Jobs.query(request, projectId);
var jobId = queryResults.jobReference.jobId;
var names = queryResults.schema.fields.map(function(field){ return field.name; });
//var records = [];
return queryResults.rows.map(function(row) {
var record = app.models.Test.newRecord();
for (var i = 0, len = names.length; i < len; ++i) {
// Calculated model should contain correspondent fields
// all non-defined fields will be ignored
record[names[i]] = (row.f[i].v);
}
return record;
});
}
It now works without error but I still get no data into my grid:
Is there something I'am missing in the configuration of the grid or the datasource??
App Maker doesn't allow to return arbitrary objects through its datasources. All results should be strongly typed:
...
return queryResults.rows.map(function(row) {
var record = app.models.CalcModelName.newRecord();
for (var i = 0, len = names.length; i < len; ++i) {
// Calculated model should contain correspondent fields
// all non-defined fields will be ignored
record[names[i]] = row.f[i].v;
}
return record;
});
Here are some samples for reference
https://developers.google.com/appmaker/samples/calculated-model/
https://developers.google.com/appmaker/samples/jdbc/

Cannot convert class to object in spreadsheet

I wish to get a couple of properties of all the folders in my GDrive and write these properties to a spreadsheet. Because of the large number of folders (over 300) I have decided to use Paging and Batch processing. This seems to be working but I can't seem to write the Array[][] I've created in the batch processing to the spreadsheet.
I'm am getting the following error when I try to set the values in my spreadsheet:
Cannot convert (class)#3cc8188e to Object[][].
I did not find any listed questions that were similar to my problem.
The last line of the script is highlighted when the error appears. Code is:
function myFunction() {
var folder = DocsList.getFolder('MyFolder');
var subfolders = null;
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var cell = sheet.getRange('a1');
var x = 0;
var pageSize = 250;
var token = null;
var xfolders = new Array(500);
do {
var resultset = folder.getFoldersForPaging(pageSize, token);
subfolders = resultset.getFolders();
token = resultset.getToken();
x = subfolders.length;
for (var a = 0; a < subfolders.length; a++) {
var contents = subfolders[a].getFiles();
xfolders[a] = new Array(6);
if(contents.length>0) {
xfolders[a][0] = subfolders[a].getName();
xfolders[a][1] = subfolders[a].getDateCreated();
xfolders[a][2] = subfolders[a].getLastUpdated();
xfolders[a][3] = contents.length;
xfolders[a][4] = subfolders[a].getSize();
xfolders[a][5] = a;
}
}
} while (subfolders.length > pageSize)
sheet.getRange(1, 1, x, 6).setValues(xfolders);
}
You've started with an array xfolders for which you've initialized a length. There's no need to do this - it's enough to know that it's an array. Later you call getRange() with rows==x, where x = subfolders.length... and if that isn't 500 (the length of xfolders) you will end up with your error. (...because you have undefined array elements.)
What you need to do is make sure that the range you're writing to has the same dimensions as the values you're presenting. One way to do that is to calculate the range dimensions using xfolders itself.
Here's your function, refactored to grow xfolders dynamically, and then to use its dimensions for output to the spreadsheet.
function myFunction() {
var folder = DocsList.getFolder('MyFolder');
var subfolders = null;
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var cell = sheet.getRange('a1');
var pageSize = 250;
var token = null;
var xfolders = [];
do {
var resultset = folder.getFoldersForPaging(pageSize, token);
subfolders = resultset.getFolders();
token = resultset.getToken();
for (var a in subfolders) {
var contents = subfolders[a].getFiles();
xfolders[a] = [];
if(contents.length>0) {
xfolders[a][0] = subfolders[a].getName();
xfolders[a][1] = subfolders[a].getDateCreated();
xfolders[a][2] = subfolders[a].getLastUpdated();
xfolders[a][3] = contents.length;
xfolders[a][4] = subfolders[a].getSize();
xfolders[a][5] = a;
}
}
} while (subfolders.length == pageSize) // Quit when we aren't getting enough folders
sheet.getRange(1, 1, xfolders.length, xfolders[0].length).setValues(xfolders);
}

Resources