I'm trying to find cells that have an error, using a Range in script. The Range consists of a single column AB of cells using Sparkline() getting data from GoogleFinance(), which quite often return Error Google Finance internal error., and display #N/A. Errors are showing:
However, the function is not returning anything when I try to getValues:
function portfolioRefreshSparklines(){
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Portfolio');
const msg = 'Refreshing...';
const err = '#N/A';
var range = sheet.getRange('Portfolio_Sparklines');
var col = range.getColumn();
var rowStart = range.getRow();
Logger.log('col: ' + col + '; rowRange: ' + rowStart);
var data = range.getValues();
for ( i=0; i<data.length; i++ ) {
// this is NOT returning the `#N/A` error (`Google Finance internal error.`)
var rv = data[i][0];
Logger.log('i: ' + i + ' rv: '+ rv)
// If an error is found, set the cell's formula to the msg, then back to the original formula.
// Think I have to reference the cell directly to do the setFormula() switch, not within the data array?
if ( rv.includes(err) ){
var row = rowStart + i;
var cell = sheet.getRange(row, col);
Logger.log('cell: ' + cell.getA1Notation() );
rv = cell.getFormula();
cell.setFormula(msg);
cell.setFormula(rv);
}
}
SpreadsheetApp.flush();
}
I've searched through the Range Class, tried to use function getDisplayValues(), but haven't found anything that returns a cell error.
Any suggestions pls?
From the question
However, the function is not returning anything when I try to getValues:
Google Finance is blocked in Google Apps Script. See Reading the values of cells that summarize Google Finance data results via Apps Script
P.S.
It doesn't make sense to include SpreadsheetApp.flush() as the last function statement. It should be used when you need to force that the changes made are applied before the function ends because you will be reading something that was changed by the script to be used in later part of it.
The Best Practices discourages the use of Google Apps Script classes (in this case var cell = sheet.getRange(row, col);) in loops because they are slow.
Related
I'm trying to run a google apps script which takes a series of importrange functions and puts them into an arrayformula query. I've successfully created a cell which actively accumulates the correct links for use in the importrange and puts it into a cell as a string. All I need the script to do is to copy that string and paste it as a formula in another cell. I can do this manually pretty easily, but I'd like to be able to set it up on a timer so it does it automatically on a certain time period.
As far as I've gotten is below and it doesn't work at all:
function Update Import Ranges() {
var spreadsheet = SpreadsheetApp.getActive();
var source = spreadsheet.getRange('B2').activate();
spreadsheet.getCurrentCell('B3').setFormula(???);
spreadsheet.getRange('B3').activate();
};
accumulates the correct links for use in the importrange and puts it into a cell as a string
The importrange() function only accepts two parameters: one for the spreadsheet to read from, and another to specify which range in the spreadsheet to return. To read from several spreadsheets and/or ranges with importrange(), you will have to create several formulas.
Alternatively, use an Apps Script function that mimics importrange() but supports multiple spreadsheets and ranges, such as the ImportMultipleRanges() function. You could call it like this:
function updateDataFromOtherSpreadsheets() {
var rangeToUpdate = "All Data!A2";
var sourceSpreadsheetKeysRange = "Config!B2:B5";
var sourceSpreadsheetDataRange = "Config!C2:C5";
var add_spreadsheet_name_prefix = false;
var add_sheet_name_prefix = false;
var ignore_blank_rows = true;
var ss = SpreadsheetApp.getActive();
var spreadsheet_keys = ss.getRange(sourceSpreadsheetKeysRange).getValues();
var range_strings = ss.getRange(sourceSpreadsheetDataRange).getValues();
var data = ImportMultipleRanges(spreadsheet_keys, range_strings, add_spreadsheet_name_prefix, add_sheet_name_prefix, ignore_blank_rows);
ss.getRange(rangeToUpdate).offset(0, 0, data.length, data[0].length).setValues(data);
}
To answer your question, you can fix the syntax errors and semantics in your function like this:
function updateImportrangeFormula() {
const sheet = SpreadsheetApp.getActiveSheet();
const ssId = sheet.getRange('B2').getValue();
const rangeA1 = sheet.getRange('C2').getValue();
const formula = '=importrange("' + ssId + '", "' + rangeA1 + '")';
sheet.getRange('B3').setFormula(formula);
}
I am fairly new to appscript and only did a little coding in college. Here is my issue:
I am trying to filter data from one sheet into two separate existing sheets. The existing sheets already have data and formulas in it. Also, the filtered data needs to update onEdit and filtered the data into the first row that does not contain text. Another caveat is that I still need to be able to manually enter data into the cells as well from time to time and I need the function to realize this and not overwrite the manually entered data.
We are using QR codes to import data for inventory and the imported data needs to be filtered. But, we need to be able to manually input new line items into these sheets still for inventory that is non related to existing QR codes.
The function will run and filter the data correctly for the most part, but the row spacing is all over the place and the first cell, typically throws a #REF error and then continues on with the function.
At this point, I'm pretty stuck.
(I know that it will need to become an onEdit(e) function later, but just need the code to work first)
Here is my function:
function myfunction(){
var ss = SpreadsheetApp;
var orderlog = ss.getActiveSpreadsheet().getSheetByName("Order Log - COMM");
var scanit = ss.getActiveSpreadsheet().getSheetByName("Scan-It Data");
var mfg = ss.getActiveSpreadsheet().getSheetByName("Order Log - MFG");
var scanlastRow = scanit.getLastRow();
var commlastrow = orderlog.getLastRow();
// For loop to filter Vendor
for(var i=1;i <= scanlastRow;i++){
// Grab the Value from the Scan-It Data Sheet
var workingcell = scanit.getRange(1 + i, 2 );
var val = workingcell.getValue();
// If the Value equals IMMCO, send it to the MFG Sheet
// If the Value is anything else, send it to the COMM Sheet
if (val == "IMMCO") {
// Grab the Scan-It Data and send it to the MFG sheet
var scanitrange = scanit.getRange(1 + i, 1 );
//var mfglastrow = mfg.getLastRow();
//mfg.insertRowAfter(mfglastrow);
var mfgrow = mfg.getRange(71 + i, 1 );
scanitrange.copyTo(mfgrow);
} else {
//Grab the Scan-It Data and send it to the last row of the COMM Sheet
var scanitrange = scanit.getRange(1 + i, 1);
var orderlogrow = orderlog.getRange(328 + i, 1 );
//orderlog.insertRowAfter(commlastrow);
//var commrange = orderlog.getRange("A327:A"+(commlastrow+1));
scanitrange.copyTo(orderlogrow);
}
}
}
I would recommend that you use a tried and tested solution such as the moveRowsFromSpreadsheetToSpreadsheet_ script. The code is almost 800 lines long so it is impractical to quote here, but it is quite simple to configure using the parameters section.
Using this macro I have as issue #NAME?. Can you help me to solve?
function macro()
{
var sheet = SpreadsheetApp.getActiveSheet();
var data = sheet.getDataRange().getValues();
for (var i = 1; i < data.length; i++)
{
var rowData = data[i];
var parameter1 = rowData[10];
var parameter2 = rowData[11];
if (parameter1 == "OK" && parameter2 != "OK")
{
sheet.getRange("M" +(i+1)).activate();
sheet.getCurrentCell().setFormula('=DATA.VALORE(A' +(i+1)+ ')+7*(6-I' +(i+1)+ ')');
}
}
}
When using Google Sheets macros (Google Apps Script) to write formulas, functions should be written in English.
This was learned from experience. There isn't official documentation about this.
Related
Google Sheets formula generated by a GAS script throws an error, even though it's correct
Error:
DATA.VALORE is not a valid function name, thus it shows #NAME. Use valid function names.
If you were meaning to use DATEVALUE, then use it instead.
That would yield into a number like 44928, use TO_DATE or format that column to DATE instead of automatic to convert that back to date.
Code:
sheet.getCurrentCell().setFormula('=TO_DATE(DATEVALUE(A' + (i + 1) + ')+7*(6-I' + (i + 1) + '))');
Output:
Reference:
Formula parse errors
DATEVALUE
TO_DATE
I'm trying to write some code using App Scripts that will (via a daily trigger), copy/paste data from the cells F13:G13 to the first empty cell in column I. Here is my code:
function TrackCurrentValues()
var spreadsheet = SpreadsheetApp.getActive();
var sheet = spreadsheet.getSheets()[0];
var lastRow = getLastRowInColumn(sheet, 'I:I');
// Logger.log('I' + parseInt(lastRow + 1));
var pasteRange = sheet.getRange('I' + parseInt(lastRow + 1) );
pasteRange.activate();
// now that we have the first empty cell in column I, paste the values we found from cells F13:G13
spreadsheet.getRange('F13:G13').copyTo(spreadsheet.getActiveRange(), SpreadsheetApp.CopyPasteType.PASTE_VALUES, false);
};
function getLastRowInColumn(sheetObj, range) {
return sheetObj.getRange(range)
.getValues().filter (String).length + 1
}
When it runs, what happens is that the data is copied from the proper location, but it's always pasted to cell A1. Moreover, the number pasted appears is prefixed with the British pound sterling character (£).
What could be wrong? It (usually) works if I run it manually. The main thing is that it doesn't find the empty cell in I:I.
This code should do what you're looking for:
function TrackCurrentValues(){
var sheet = SpreadsheetApp.getActive().getSheets()[0];
var lastRow = getLastRowInColumn(sheet, 'I:I');
var pasteRange = sheet.getRange(lastRow + 1,9,1,2);
var copyRange = sheet.getRange(13,6,1,2)
pasteRange.setValues(copyRange.getValues())
};
function getLastRowInColumn(sheetObj, range) {
return sheetObj.getRange(range).getValues().filter(String).length
}
On your £ chracter question, that range is likely formatted to display that currency symbol. To update this, select the range, go to the toolbar and select Format > Number > Specify the format you would like
Additional Thoughts:
i) You are adding one to lastRow variable twice (once in getLastRowInCOlumn function and again in pasteRange definition)
ii) I would reocmmend not using "active ranges" to store a location, instead store that range in a variable
iii) It seems your copy range was 2 columns wide but your pasteRange was only 1 column wide
I have a sheet that want to import value value of Google search result using importxml function. Sometimes I get #N/A in cell A2.
I used while loop to keep trying to fetch the data and still I don't get data even if I wait for long time. Is it possible to setup a JavaScript timer so runs until I get value in cell A2 then timer stops and allow the rest of code to continue?
What should I do in order to avoid such cases and always get value on cell A2? Any Alternative solution?
var queryString = Math.random();
var cellFunction1 = '=IMPORTXML("' + SpreadsheetApp.getActiveSheet().getRange('C2').getValue() + '&randomNumber=' + queryString + '","'+ SpreadsheetApp.getActiveSheet().getRange('D2').getValue() + '")';
SpreadsheetApp.getActiveSheet().getRange('A2').setValue(cellFunction1);
var stop = 0;
while (SpreadsheetApp.getActiveSheet().getRange('A2').getValue() === "#N/A" && stop++<10) {
Utilities.sleep(5000);
var queryString3 = Math.random();
var cellFunction1 = '=IMPORTXML("' + SpreadsheetApp.getActiveSheet().getRange('C2').getValue() + '&randomNumber=' + queryString3 + '","'+ SpreadsheetApp.getActiveSheet().getRange('D2').getValue() + '")';
SpreadsheetApp.getActiveSheet().getRange('A2').setValue(cellFunction1);
}
I faced the same problem. I suspect it's because the code tries to .getValue from the cells before the cells' values are updated from IMPORTXML.
My workaround was to write two separate functions, one to force the IMPORTXML to refresh, and then the other to write the data into the sheet. Then I gave the IMPORTXML a trigger to run every 5 minutes, and then the write function with its own trigger.
You might want to try this:
function refresher() {
var ss = SpreadsheetApp.getActiveSheet();
var sheet = ss.getSheetById("input");
// Cell input!A2 holds the formula '=IMPORTXML(A1, XMLpath)'
//So now we give input!A1 a URL with a random ?number behind...
//...to force IMPORTXML to refresh
sheet.getRange(1, 1)
.setValue("http://www.urlWhereInfoResides.com/?" + Math.floor(Math.random() * 40));
}
^ Give refresher() a trigger to run every 5 minutes
function writer() {
var ss2 = SpreadsheetApp.getActiveSheet();
//assumes desired value sits in cell input!A2
var input = ss2.getSheetById("input")
.getRange(2, 1)
.getValue();
//adds input value into a new row in output sheet
var outputSheet = ss2.getSheetById("output");
var output = outputSheet.getRange(outputSheet.getLastRow() + 1, 1)
.setValue(input);
}
^ Give writer() a trigger to run every 5 minutes or whatever is needed