I have added a custom menu and script to my google sheets page. I get an error saying Script function not found, followed by showing me my entire script! What have I done wrong?
Error:
Script function not found: function mergeDuplicates() { var target =
SpreadsheetApp.getActiveSpreadsheet(); var sheet =
target.getSheetByName("Test"); var lastRow = sheet.getLastRow(); var
dataRange = sheet.getRange(2, 1, lastRow - 1, 2); var data =
dataRange.getValues(); var notesByName = {}; for (var i = 0; i <
data.length; i++) { var row = data[i]; var curName = row[0]; var curNote
= row[1]; if (!curName.trim()) { continue; } if (!notesByName[curName])
{ notesByName[curName] = String(curNote); } else {
notesByName[curName].concat(curNote); } } var outputData =
Object.keys(notesByName).map(function (name) { return [name,
notesByName[name]]; }); dataRange.clearContent(); var newDataRange =
sheet.getRange(2, 1, outputData.length, 2);
newDataRange.setValues(outputData); } For more information, see
https://developers.google.com/apps-
script/reference/base/menu#addItem(String,String)`
Code:
function mergeDuplicates()
{
var target = SpreadsheetApp.getActiveSpreadsheet();
var sheet = target.getSheetByName("Test");
var lastRow = sheet.getLastRow();
var dataRange = sheet.getRange(2, 1, lastRow-1, 2);
var data = dataRange.getValues();
var notesByName = {};
for (var i = 0; i < data.length; i++)
{
var row = data[i];
var curName = row[0];
var curNote = row[1];
// empty name
if (!curName.trim())
{
continue;
}
// if name found first time, save it to object
if (!notesByName[curName])
{
notesByName[curName] = String(curNote);
}
// if duplicate, sum numbers
else
{
notesByName[curName].concat(curNote);
}
}
// prepare data for output
var outputData = Object.keys(notesByName).map(function(name){
return [name, notesByName[name]];
});
// clear old data
dataRange.clearContent();
// write calculated data
var newDataRange = sheet.getRange(2, 1, outputData.length, 2);
newDataRange.setValues(outputData);
}
function onOpen() {
var ui = SpreadsheetApp.getUi();
var menu = ui.createMenu('Custom Menu');
var item = menu.addItem('Merge Duplicates', mergeDuplicates);
item.addToUi();
}
What does your menu code look like? I'm guessing it's a problem with the way you did the addItem in one of your menus. Perhaps your additem looks like this addItem('Description','mergeDuplicates()') rather than this addItem('Description','mergeDuplicates')
Related
I found a couple of functions that met my needs, but they don't want to play nice together. The goal is to pull data once a day, then add it to a new column. The history function is fine, and I've got a trigger setup to run it once a day, but it's erroring when attempting to run automatically, and sending me an email with the following.
6/18/18 5:29 PM loadRegionAggregates TypeError: Cannot find function
forEach in object [object Object]. (line 19, file
"Code") time-based 6/18/18 5:29 PM
here's the full code.gs (I bolded the section with line 19, first line of the function)
// Requires a list of typeids, so something like Types!A:A
// https://docs.google.com/spreadsheets/d/1IixV0eNqg19FE6cLzb83G1Ucb0Otl-Jnvm6csAlPKwo/edit?usp=sharing for an example
function loadRegionAggregates(priceIDs,regionID){
if (typeof regionID == 'undefined'){
regionID=10000002;
}
if (typeof priceIDs == 'undefined'){
throw 'Need a list of typeids';
}
var prices = new Array();
var dirtyTypeIds = new Array();
var cleanTypeIds = new Array();
var url="https://market.fuzzwork.co.uk/aggregates/?station=60003760&types=34,35,36,37,38,39,40"
**priceIDs.forEach(function (row) {
row.forEach(function (cell) {
if (typeof(cell) === 'number' ) {
dirtyTypeIds.push(cell);
}**
});
});
cleanTypeIds = dirtyTypeIds.filter(function(v,i,a) {
return a.indexOf(v)===i;
});
prices.push(['TypeID','Buy volume','Buy Weighted Average','Max Buy','Min Buy','Buy Std Dev','Median Buy','Percentile Buy Price','Sell volume','Sell Weighted Average','Max sell','Min Sell','Sell Std Dev','Median Sell','Percentile Sell Price'])
var parameters = {method : "get", payload : ""};
var o,j,temparray,chunk = 100;
for (o=0,j=cleanTypeIds.length; o < j; o+=chunk) {
temparray = cleanTypeIds.slice(o,o+chunk);
Utilities.sleep(100);
var types=temparray.join(",").replace(/,$/,'')
var jsonFeed = UrlFetchApp.fetch(url+types, parameters).getContentText();
var json = JSON.parse(jsonFeed);
if(json) {
for(i in json) {
var price=[parseInt(i),
parseInt(json[i].buy.volume),
parseInt(json[i].buy.weightedAverage),
parseFloat(json[i].buy.max),
parseFloat(json[i].buy.min),
parseFloat(json[i].buy.stddev),
parseFloat(json[i].buy.median),
parseFloat(json[i].buy.percentile),
parseInt(json[i].sell.volume),
parseFloat(json[i].sell.weightedAverage),
parseFloat(json[i].sell.max),
parseFloat(json[i].sell.min),
parseFloat(json[i].sell.stddev),
parseFloat(json[i].sell.median),
parseFloat(json[i].sell.percentile)];
prices.push(price);
}
}
}
return prices;
}
function storeData() {
var sheet = SpreadsheetApp.getActiveSheet();
var datarange = sheet.getDataRange();
var numRows = datarange.getNumRows();
var numColumns = datarange.getNumColumns();
sheet.getRange(1,numColumns + 1).setValue(new Date());
for (var i=2; i <= numRows; i++) {
var prices = sheet.getRange(i, 8).getValue();
sheet.getRange(i, numColumns + 1).setValue(prices);
}
}
I found this link:
TypeError: Cannot find function forEach in object
Which explains some of it, but when the code runs in debug mode, I don't have a range defined, and then the live sheet bugs with:
ReferenceError: "row" is not defined (line 21).
Note: My original code works fine when I run the script locally, just not when the daily timer goes off. I'm assuming the issues listed on the linked query regarding no forEach class on the nested object are applying to whatever runs the triggered .gs
Edit: Ok so i've been working on this all day. I've learned how to set an array var (since I didn't need to change the array, static works) and i've been trying to get the rest of the code adjusted ever since. Here's where I am so far:
// Requires a list of typeids, so something like Types!A:A
// https://docs.google.com/spreadsheets/d/1IixV0eNqg19FE6cLzb83G1Ucb0Otl-Jnvm6csAlPKwo/edit?usp=sharing for an example
function loadRegionAggregates(priceIDs,regionID){
if (typeof regionID == 'undefined'){
regionID=10000002;
}
if (typeof priceIDs == 'undefined'){
priceIDs = [34,35,36,37,38,39,40,11399];
}
var prices = new Array();
var dirtyTypeIds = new Array();
var cleanTypeIds = new Array();
var url="https://market.fuzzwork.co.uk/aggregates/?station=60003760&types=34,35,36,37,38,39,40"
// for (var priceIDs) {
// if (row.hasOwnProperty(column)) {
// var cell = row[column];
// if (typeof cell == "number") {
priceIDs.push(priceIDs);
// }
// }
// }
cleanTypeIds = dirtyTypeIds.filter(function(v,i,a) {
return a.indexOf(v)===i;
});
prices.push(['TypeID','Buy volume','Buy Weighted Average','Max Buy','Min Buy','Buy Std Dev','Median Buy','Percentile Buy Price','Sell volume','Sell Weighted Average','Max sell','Min Sell','Sell Std Dev','Median Sell','Percentile Sell Price'])
var parameters = {method : "get", payload : ""};
var o,j,temparray,chunk = 100;
for (o=0,j=cleanTypeIds.length; o < j; o+=chunk) {
temparray = cleanTypeIds.slice(o,o+chunk);
Utilities.sleep(100);
var types=temparray.join(",").replace(/,$/,'')
var jsonFeed = UrlFetchApp.fetch(url+types, parameters).getContentText();
var json = JSON.parse(jsonFeed);
if(json) {
for(i in json) {
var price=[parseInt(i),
parseInt(json[i].buy.volume),
parseInt(json[i].buy.weightedAverage),
parseFloat(json[i].buy.max),
parseFloat(json[i].buy.min),
parseFloat(json[i].buy.stddev),
parseFloat(json[i].buy.median),
parseFloat(json[i].buy.percentile),
parseInt(json[i].sell.volume),
parseFloat(json[i].sell.weightedAverage),
parseFloat(json[i].sell.max),
parseFloat(json[i].sell.min),
parseFloat(json[i].sell.stddev),
parseFloat(json[i].sell.median),
parseFloat(json[i].sell.percentile)];
prices.push(price);
}
}
}
return prices;
}
function storeData() {
var sheet = SpreadsheetApp.getActiveSheet();
var datarange = sheet.getDataRange();
var numRows = datarange.getNumRows();
var numColumns = datarange.getNumColumns();
sheet.getRange(1,numColumns + 1).setValue(new Date());
for (var i=2; i <= numRows; i++) {
var prices = sheet.getRange(i, 8).getValue();
sheet.getRange(i, numColumns + 1).setValue(prices);
}
}
So far, so good. It's not throwing any more errors on debug, and the column headers are coming in, but it's not bringing in the data anymore from the push.
Help!
Can someone please help me with this script?
function HistoryTrigger() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
if (sheet.getName() == "Status") {
var activeCell = sheet.getRange("G11");
if (activeCell.getValue() ="OK") {
recordHistory();
}
}
}
function recordHistory() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Historie");
var source = sheet.getRange("A2:B2");
var values = source.getValues();
values[0][0] = new Date();
sheet.appendRow(values[0]);
};
I want the trigger to start when Status!G11="OK", then start recordHistory().
try:
if (activeCell.getValue() =="OK") {
double == is proper syntax for if statements.
You may run this functoin with onEdit:
function onEdit(e) {
if (e.range.getSheet().getName() === 'Status') { HistoryTrigger(); }
}
It'll check each time user change something. It will run when G11 is OK on sheet 'Status'.
To combine all together, I suggest passing e into HistoryTrigger:
function onEdit(e) {
HistoryTrigger(e);
}
function HistoryTrigger(e) {
var ss = e.range.getSheet();
if (ss.getName() === 'Status') {
var activeCell = ss.getRange("G11");
if (activeCell.getValue() == "OK") {
recordHistory();
}
}
}
function recordHistory() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Historie");
var source = sheet.getRange("A2:B2");
var values = source.getValues();
values[0][0] = new Date();
sheet.appendRow(values[0]);
};
I have got following script in Google docs spreadsheet which is fetching data from Fitbit. Script worked fine so far but recently on 6th July Google stopped using OAuthConfig so script is not working since:-(
I am not programmer, I am just advanced user. So I would like to kindly ask some programmer to help tune script below in order to make it work again.
// Key of ScriptProperty for Firtbit consumer key.
var CONSUMER_KEY_PROPERTY_NAME = "fitbitConsumerKey";
// Key of ScriptProperty for Fitbit consumer secret.
var CONSUMER_SECRET_PROPERTY_NAME = "fitbitConsumerSecret";
// Default loggable resources (from Fitbit API docs).
var LOGGABLES = ["activities/log/steps", "activities/log/distance",
"activities/log/activeScore", "activities/log/activityCalories",
"activities/log/calories", "foods/log/caloriesIn",
"activities/log/minutesSedentary",
"activities/log/minutesLightlyActive",
"activities/log/minutesFairlyActive",
"activities/log/minutesVeryActive", "sleep/timeInBed",
"sleep/minutesAsleep", "sleep/minutesAwake", "sleep/awakeningsCount",
"body/weight", "body/bmi", "body/fat",];
// function authorize() makes a call to the Fitbit API to fetch the user profile
function authorize() {
var oAuthConfig = UrlFetchApp.addOAuthService("fitbit");
oAuthConfig.setAccessTokenUrl("https://api.fitbit.com/oauth/access_token");
oAuthConfig.setRequestTokenUrl("https://api.fitbit.com/oauth/request_token");
oAuthConfig.setAuthorizationUrl("https://api.fitbit.com/oauth/authorize");
oAuthConfig.setConsumerKey(getConsumerKey());
oAuthConfig.setConsumerSecret(getConsumerSecret());
var options = {
"oAuthServiceName": "fitbit",
"oAuthUseToken": "always",
};
// get the profile to force authentication
Logger.log("Function authorize() is attempting a fetch...");
try {
var result = UrlFetchApp.fetch("https://api.fitbit.com/1/user/-/profile.json", options);
var o = Utilities.jsonParse(result.getContentText());
return o.user;
}
catch (exception) {
Logger.log(exception);
Browser.msgBox("Error attempting authorization");
return null;
}
}
// function setup accepts and stores the Consumer Key, Consumer Secret, firstDate, and list of Data Elements
function setup() {
var doc = SpreadsheetApp.getActiveSpreadsheet();
var app = UiApp.createApplication().setTitle("Setup Fitbit Download");
app.setStyleAttribute("padding", "10px");
var consumerKeyLabel = app.createLabel("Fitbit OAuth Consumer Key:*");
var consumerKey = app.createTextBox();
consumerKey.setName("consumerKey");
consumerKey.setWidth("100%");
consumerKey.setText(getConsumerKey());
var consumerSecretLabel = app.createLabel("Fitbit OAuth Consumer Secret:*");
var consumerSecret = app.createTextBox();
consumerSecret.setName("consumerSecret");
consumerSecret.setWidth("100%");
consumerSecret.setText(getConsumerSecret());
var firstDate = app.createTextBox().setId("firstDate").setName("firstDate");
firstDate.setName("firstDate");
firstDate.setWidth("100%");
firstDate.setText(getFirstDate());
// add listbox to select data elements
var loggables = app.createListBox(true).setId("loggables").setName(
"loggables");
loggables.setVisibleItemCount(4);
// add all possible elements (in array LOGGABLES)
var logIndex = 0;
for (var resource in LOGGABLES) {
loggables.addItem(LOGGABLES[resource]);
// check if this resource is in the getLoggables list
if (getLoggables().indexOf(LOGGABLES[resource]) > -1) {
// if so, pre-select it
loggables.setItemSelected(logIndex, true);
}
logIndex++;
}
// create the save handler and button
var saveHandler = app.createServerClickHandler("saveSetup");
var saveButton = app.createButton("Save Setup", saveHandler);
// put the controls in a grid
var listPanel = app.createGrid(6, 3);
listPanel.setWidget(1, 0, consumerKeyLabel);
listPanel.setWidget(1, 1, consumerKey);
listPanel.setWidget(2, 0, consumerSecretLabel);
listPanel.setWidget(2, 1, consumerSecret);
listPanel.setWidget(3, 0, app.createLabel(" * (obtain these at dev.fitbit.com)"));
listPanel.setWidget(4, 0, app.createLabel("Start Date for download (yyyy-mm-dd)"));
listPanel.setWidget(4, 1, firstDate);
listPanel.setWidget(5, 0, app.createLabel("Data Elements to download:"));
listPanel.setWidget(5, 1, loggables);
// Ensure that all controls in the grid are handled
saveHandler.addCallbackElement(listPanel);
// Build a FlowPanel, adding the grid and the save button
var dialogPanel = app.createFlowPanel();
dialogPanel.add(listPanel);
dialogPanel.add(saveButton);
app.add(dialogPanel);
doc.show(app);
}
// function sync() is called to download all desired data from Fitbit API to the spreadsheet
function sync() {
// if the user has never performed setup, do it now
if (!isConfigured()) {
setup();
return;
}
var user = authorize();
// Spatny kod, oprava nize - var doc = SpreadsheetApp.getActiveSpreadsheet();
var doc = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Pavel');
doc.setFrozenRows(1);
var options = {
"oAuthServiceName": "fitbit",
"oAuthUseToken": "always",
"method": "GET"
};
// prepare and format today's date, and a list of desired data elements
var dateString = formatToday();
var activities = getLoggables();
// for each data element, fetch a list beginning from the firstDate, ending with today
for (var activity in activities) {
var currentActivity = activities[activity];
try {
var result = UrlFetchApp.fetch("https://api.fitbit.com/1/user/-/"
+ currentActivity + "/date/" + getFirstDate() + "/"
+ dateString + ".json", options);
} catch (exception) {
Logger.log(exception);
Browser.msgBox("Error downloading " + currentActivity);
}
var o = Utilities.jsonParse(result.getContentText());
// set title
var titleCell = doc.getRange("a1");
titleCell.setValue("date");
var cell = doc.getRange('a2');
// fill the spreadsheet with the data
var index = 0;
for (var i in o) {
// set title for this column
var title = i.substring(i.lastIndexOf('-') + 1);
titleCell.offset(0, 1 + activity * 1.0).setValue(title);
var row = o[i];
for (var j in row) {
var val = row[j];
cell.offset(index, 0).setValue(val["dateTime"]);
// set the date index
cell.offset(index, 1 + activity * 1.0).setValue(val["value"]);
// set the value index index
index++;
}
}
}
}
function isConfigured() {
return getConsumerKey() != "" && getConsumerSecret() != "";
}
function setConsumerKey(key) {
ScriptProperties.setProperty(CONSUMER_KEY_PROPERTY_NAME, key);
}
function getConsumerKey() {
var key = ScriptProperties.getProperty(CONSUMER_KEY_PROPERTY_NAME);
if (key == null) {
key = "";
}
return key;
}
function setLoggables(loggable) {
ScriptProperties.setProperty("loggables", loggable);
}
function getLoggables() {
var loggable = ScriptProperties.getProperty("loggables");
if (loggable == null) {
loggable = LOGGABLES;
} else {
loggable = loggable.split(',');
}
return loggable;
}
function setFirstDate(firstDate) {
ScriptProperties.setProperty("firstDate", firstDate);
}
function getFirstDate() {
var firstDate = ScriptProperties.getProperty("firstDate");
if (firstDate == null) {
firstDate = "2012-01-01";
}
return firstDate;
}
function formatToday() {
var todayDate = new Date;
return todayDate.getFullYear()
+ '-'
+ ("00" + (todayDate.getMonth() + 1)).slice(-2)
+ '-'
+ ("00" + todayDate.getDate()).slice(-2);
}
function setConsumerSecret(secret) {
ScriptProperties.setProperty(CONSUMER_SECRET_PROPERTY_NAME, secret);
}
function getConsumerSecret() {
var secret = ScriptProperties.getProperty(CONSUMER_SECRET_PROPERTY_NAME);
if (secret == null) {
secret = "";
}
return secret;
}
// function saveSetup saves the setup params from the UI
function saveSetup(e) {
setConsumerKey(e.parameter.consumerKey);
setConsumerSecret(e.parameter.consumerSecret);
setLoggables(e.parameter.loggables);
setFirstDate(e.parameter.firstDate);
var app = UiApp.getActiveApplication();
app.close();
return app;
}
// function onOpen is called when the spreadsheet is opened; adds the Fitbit menu
function onOpen() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var menuEntries = [{
name: "Sync",
functionName: "sync"
}, {
name: "Setup",
functionName: "setup"
}, {
name: "Authorize",
functionName: "authorize"
}];
ss.addMenu("Fitbit", menuEntries);
}
// function onInstall is called when the script is installed (obsolete?)
function onInstall() {
onOpen();
}
Problem solved with updated code at https://github.com/loghound/Fitbit-for-Google-App-Script
Learning Dart.lang . Have a php background. Given this code at this link...
Link to full working php algorithm
or to copy the main part here, this is what i got in PHP and works fine. I need to know how to do it in dart:
foreach ($floors as $x) {
$temp = explode("," , $x);
$floor[$temp[0]][$temp[1]] = $temp[2];
}
library map;
void main() {
var temp = """
1,201,70
1,301,71
3,301,31
1,401,79
2,501,2
1,601,171
2,801,61
2,901,100
5,101,54
4,203,23
3,201,112""";
var values = temp.split('\n');
var floors = new Map<int, Map<int, double>>();
values.forEach((f) {
var v = f.trim().split(',');
var fk = int.parse(v[0]);
var rk = int.parse(v[1]);
var rv = double.parse(v[2]);
if(floors[fk] == null) {
floors[fk] = new Map<int,double>();
}
floors[fk][rk]=rv;
});
floors.forEach((fk, fv) {
var tempSum = 0;
var lowest = null;
for(var rk in fv.keys) {
var temp = fv[rk];
tempSum += temp;
if(lowest == null || (lowest > temp)) {
lowest = temp;
}
}
print("Floor #${fk} has ${fv.length} rooms. The average temp is ${tempSum / fv.length}. The lowest temp is ${lowest}.");
});
}
i'm writing a little google apps script which allow to select several names among a list of developpers. But in my doPost(e) method, i can't access to the array of posted values (it's undefinded), event if i check all the checkboxes...
function onOpen() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
// add the new menu
var menuEntries = [];
menuEntries.push({name: "Edit page", functionName: "start"})
ss.addMenu("My tools", menuEntries);
}
function start() {
var app = UiApp.createApplication();
app.setTitle("Edit page");
var formPanel = app.createFormPanel();
var mainPanel = app.createVerticalPanel();
var gridPanel = app.createGrid(1, 2);
// developpers
var developperPanel = app.createVerticalPanel()
var developpers = [];
developpers.push( "John", "Peter", "Max", "Johnny" );
for (var i = 1; i < developpers.length; ++i) {
var checkbox = app.createCheckBox(developpers[i]).setName("dev"+i);
developperPanel.add(checkbox);
}
gridPanel.setWidget(0,0,app.createLabel("Developpers : "));
gridPanel.setWidget(0,1,developperPanel);
// submit button
mainPanel.add(gridPanel);
mainPanel.add(app.createSubmitButton().setText("OK"));
formPanel.add(mainPanel);
app.add(formPanel);
var ss = SpreadsheetApp.getActive();
ss.show(app);
}
function doPost(e) {
var app = UiApp.getActiveApplication();
app.add(app.createLabel(e.values[0])); // Here is the error.
return app;
}
In the exemple, the list is fixed but in my real script, i create the list thanks to the Spreadsheet.
Thanks
Max
The correct way to see the checkboxes values is using e.parameter[name], like you said yourself on a comment. Here is some code example:
function start() {
//...
var developpers = ["John", "Peter", "Max", "Johnny"];
//you were skiping the first developer
for (var i = 0; i < developpers.length; ++i) { //array starts on zero, not one
var checkbox = app.createCheckBox(developpers[i]).setName("dev"+i);
developperPanel.add(checkbox);
}
//...
}
function doPost(e) {
var app = UiApp.getActiveApplication();
var developpers = ["John", "Peter", "Max", "Johnny"]; //repeat it here or make it global, if it's static like this
var checked = [];
for( var i in developpers )
if( e.parameter['dev'+i] == 'true' )
checked.push(developpers[i]);
app.add(app.createLabel(checked));
return app;
}
You should use e.parameter.yourCheckBoxName
for example:
function doPost(e) {
var app = UiApp.getActiveApplication();
app.add(app.createLabel(e.parameter.dev1); // Access checkbox by its name
return app;
}
This would show status of check box "Peter", when it is checked. You can modify based on your need.