Get notes from a cell and insert into another cell - google-apps-script

I have a range of cells in Google Sheets, some of them have notes attached.
If there's a note attached to a cell, I need to put the note in a separate cell, and put the location of that note in another cell.
I found this script elsewhere:
function getNote(cell)
{
var ss = SpreadsheetApp.getActiveSpreadsheet();
var range = ss.getRange(cell)
return range.getNote();
}
but when I try to use it I get an error "Exception: Range not found (line 12)."
But this script only gets me halfway there as it only gets the note and puts it in a cell. I also need to know what cell the note came from.
Any help is greatly appreciated.

In the script above the function getNote() expects the paramter cell
If you just run the script without calling getNote() from another function / an environment where it gets a values for cell assigned, the script will fail wiht the error you obtained.
Indeed, this script doe snot not meet your needs. What you probably want is to screen all your cells for the one that have notes.
What you need to decide is into which cells you want to put the note and the cell notation.
Below is a sample that you need to adapt for your needs:
function getNotes() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
//if you have only one sheet in the spreadsheet, otherwise use ss.getSheetByName(name);
var sheet = ss.getActiveSheet();
var range = sheet.getDataRange();
var results = range.getNotes();
for (var i = 0; i < results.length; i++) {
for (var j = 0; j < results[0].length; j++) {
//if a not empty note was found:
if(results[i][j]){
var note = results[i][j];
var cell = range.getCell(i+1, j+1);
var notation = cell.getA1Notation();
//adjust the offset as function of the column / row where you want to output the results
cell.offset(0, 1).setValue(note);
cell.offset(0, 2).setValue(notation);
}
}
}
}
Important references:
getNotes()
getA1Notation()
getDataRange()
getCell(row, column)
offset(rowOffset, columnOffset)

Related

Use OnChange function only on the cell that has changed

I'm using the following script to provide the hex color code of column A:
function onChange(e) {
if (e.changeType == "FORMAT") {
var formula = "=GetCellColorCode";
var tempFormula = "=sample";
var sheet = e.source.getActiveSheet();
sheet.createTextFinder(`^\\${formula}`).matchFormulaText(true).useRegularExpression(true).replaceAllWith(tempFormula);
sheet.createTextFinder(`^\\${tempFormula}`).matchFormulaText(true).useRegularExpression(true).replaceAllWith(formula);
}
}
function GetCellColorCode(input)
{
var ss = SpreadsheetApp.getActiveSpreadsheet();
var cell = ss.getRange(input);
var result = cell.getBackground();
return result
}
I'm using =GetCellColorCode("A"&ROW()) in column G. However, it spans hundreds of rows, and takes too long to update, as every instance of the formula updates when one cell colour background is changed. Is there a way to change this script so only the formula on the row where the cell background colour has changed is updated? I've tried using replaceWith rather than replaceAllWith, but it doesn't update at all. Thanks in advance!
EDIT: For further context, I need this speeding up as I also have a script in there that updates my filter (I want to filter out all green and red rows), but it seems to time out frequently so the filter isn't often updated:
function update_filter() {
var col = 7;
var filter = SpreadsheetApp.getActiveSheet().getFilter();
var criteria = filter.getColumnFilterCriteria(col);
filter.setColumnFilterCriteria(col, criteria);
}
active range usually provides the range where change took place. Try
var sheet1/*renamed from sheet*/ = e.source.getActiveSheet();
var sheet = sheet1.getRange(sheet1.getActiveRange().getRow(),7);
The variable sheet is now of range type, which points to the active row's G column.

How to set formula with condition if the cell is blank with google app script?

So I have multiple files that have a column where I would like to update in the formula. However, there might be a certain cell that already has a value in it, but I don't want to replace it with the formula (see screenshot for reference).
I read some references here, but haven't found a similar case like mine.
This is the attempt that I do, but it's not working:
function updateWithFormula(){
/*** Input Data From Multiple Sources ****/
var sourceWorkbook = SpreadsheetApp.openById('')//id of the workbook
//Open tab 'Sheet1' and pull the data inside the script
var sourceSheet = sourceWorkbook.getSheetByName('Sheet1')
var source = sourceSheet.getDataRange().getDisplayValues()
for(row in source){
if (source[row][3]=="Update Value") {
//open files through link
var files = SpreadsheetApp.openByUrl(source[row][2]) //there's a link inside this column that linked to the file that I want to update
/*******insert formula *******/
//get range that want to be inserted by the formula, which is column S
//if the column S already have value in it, I don't want to do anything in it, however if it doesn't have value, I would like to put a formula
var result = files.getSheetByName('Sheet1').getRange("S2:S") //this is the column that I want to update
//set formula
for(r in result)
{
if(result[r] == "")
result[r].setFormula("=R"+ r+1)
}
}
}
}
Do you guys have any idea why my code is not working? Any advice for this case?
Thank you!
Objective
If I understood correctly, your objectives are the following:
Retrieve data from a "master" spreadsheet with information on which spreadsheets to update.
Loop through said data and locate the spreadsheets (represented as rows) that require updating.
Open those spreadsheets individually.
Update those spreadsheets rows with a sheets formula if a certain condition is met (in this case, that the cell is blank).
Issues
The for(var a in b) syntax in javaScript is used to iterate through object, not arrays. You should change it to:
for (var i = 0; i<source.length; i++){
//YOUR CODE
}
where: source[i] lets you access that specific row.
When you try to get the individual sheets' values, you are actually only getting the range, not the values themselves. You should replace this:
var result = files.getSheetByName('Sheet1').getRange("S2:S")
with this:
var sheet = files.getSheetByName('Sheet1');
var range = sheet.getRange("S2:S");
var values = range.getValues();
(You can read more about ranges and how they work here).
To input values into a spreadsheet, you should do it by using the setValue() method in the range class. Again, go here for more info. So, instead of:
result[r].setFormula("=R"+ r+1)
use:
var rangeToModify = sheet.getRange(j, 19); //LETTER S IS THE 19TH
rangeToModify.setValue("=R"+ (j+1)); //SET THE FORMULA
Final Code
function updateWithFormula(){
var sourceWorkbook = SpreadsheetApp.openById('')//id of the workbook
//Open tab 'Sheet1' and pull the data inside the script
var sourceSheet = sourceWorkbook.getSheetByName('Sheet1')
var source = sourceSheet.getDataRange().getDisplayValues()
for(var i = 0; i<source.length; i++){
if (source[i][3]=="Update Value"){
var files = SpreadsheetApp.openByUrl(source[row][2]);
var sheet = files.getSheetByName('Sheet1');
var range = sheet.getRange("S2:S");
var values = range.getValues();
//set formula
for(var j = 0; j<values.length; j++){
if (values[j] == ""){
//GET THE RANGE THAT YOU WANT TO MODIFY
var rangeToModify = sheet.getRange(j, 19); //LETTER S IS THE 19TH
rangeToModify.setValue("=R"+ (j+1)); //SET THE FORMULA
}
}
}
}
}
I believe your current situation and your goal are as follows.
"Sheet1" of sourceWorkbook has the Spreadsheet URLs and the value of "Update Value" in the columns "C" and "D", respectively.
You want to retrieve the Spreadsheet from the URL, and want to check the column "S2:S" of of "Sheet1" in the retrieved Spreadsheet, and want to put a formula like "=R"+ r+1 to the non-empty cells of the column "S".
In this case, how about the following modification?
Modification points:
var result = files.getSheetByName('Sheet1').getRange("S2:S") returns Class Range object. This cannot be used with for(r in result). This is the reason of but it's not working. This has already been mentioned by the Oriol Castander's answer.
When setFormula is used in a loop, the process cost becomes high.
When these points are reflected in your script, it becomes as follows.
Modified script:
function updateWithFormula() {
var sourceWorkbook = SpreadsheetApp.openById(''); // Please set your Spreadsheet ID.
var sourceSheet = sourceWorkbook.getSheetByName('Sheet1');
var source = sourceSheet.getDataRange().getDisplayValues();
source.forEach(r => {
if (r[3] == "Update Value") {
var sheet = SpreadsheetApp.openByUrl(r[2]).getSheetByName("Sheet1");
var rangeList = sheet.getRange("S2:S" + sheet.getLastRow()).getDisplayValues().flatMap(([e], i) => e == "" ? [`S${i + 2}`] : []);
if (rangeList.length > 0) {
sheet.getRangeList(rangeList).setFormulaR1C1("=R[0]C[-1]");
}
}
});
}
In this modification, the formula is put as the R1C1 using the range list. By this, I thought that the process cost will be able to be reduced a little.
References:
getRangeList(a1Notations)
setFormulaR1C1(formula)

Is there a script to Clear specific cells but not the formula?

spreadsheet attached
The script below to clear fields works however, my formulas are also deleted.
I simply want to ensure the formulas are protected.
The formulas return information for the id entered into the cell (f16).
function clear1() {
var sheet = SpreadsheetApp.getActive().getSheetByName('Data Lookup');
var rangesToClear = ['F6', "F8", "F10", "F12", "F14", "F16"];
for (var i=0; i<rangesToClear.length; i++) {
sheet.getRange(rangesToClear[i]).clearContent();
var getCar = sheet.getRange("D6").getValue();
sheet.getRange("F6").setValue(getCar);
Logger.log(getCar)
}
}
I might be totally missing what you are trying to do, but I would leave the formulas and just remove the value in F16. The formulas would still look for a blank cell and show the results of the blank ID - also blank.
Or change the formulas to IF(F16="","",yourformula)
The filter() formulas will error out when the search key is blank, because it will match all blank rows in Data. Replace them with vlookup() formulas like this:
=iferror(vlookup(F16, { Data!F2:F, Data!A2:A }, 2, false))
Use Data > Protected sheets and ranges to protect the range F6:G14.
In your script, only clear cell F16, and leave other cells untouched.
Modified script
function clear1() {
var sheet = SpreadsheetApp.getActive().getSheetByName('Data Lookup');
var rangesToClear = ["F14", "F16"];
for (var i=0; i<rangesToClear.length; i++) {
sheet.getRange(rangesToClear[i]).clearContent();
}
}
One change made here was that values were removed rangesToClear array. Previously it was:
var rangesToClear = ['F6', "F8", "F10", "F12", "F14", "F16"];
And now its:
var rangesToClear = ["F14", "F16"];
Which I believe corresponds to the "Phone" and "ID number" fields in your screen shot.
So now the script will only clear the ranges that don't have formulae.
The other change I made was to delete this:
var getCar = sheet.getRange("D6").getValue();
sheet.getRange("F6").setValue(getCar);
Logger.log(getCar)
As this seems to fetch a value from D6 which in you example is just the lable "Date:" (I don't know why the variable is called getCar), and then puts that in F6, which you seem to want to keep as a formula.
Reference
Range Methods

is it possible to use getRowsData without a range?

I am using Google app scrips and I want to iterate through a spreadsheet that will be updated weekly. (this is why i dont want to set a range i want to be able to iterate through the entire sheet.)
Is this possible? If yes can you give an example of how this would be done?
and if this isn't a good idea, why not?
Thank you!
Example code
function doGet(e){
var ss = SpreadsheetApp.openById('key in here');
var sheet = ss.getSheetByName("Form Responses");
var range = sheet.getRange(1,1);
var dataRange = range.getValue().toString();
app.add(app.createHTML("There are " + dataRange + " posts today"))
return app;
Something like this, but I want to be able to see the whole sheet not just the range
Henrique Abreu and Srik provided you with correct answer - getDataRange on sheet should serve your purpose.
function doGet(e){
var sheet = SpreadsheetApp.openById('spreadsheetId').getSheetByName('sheetName');
var data = sheet.getDataRange().getValues();;
...
}
data is now 2-dimensional array, you can iterate through it, get last column/row containing content etc. Please read through class reference here: https://developers.google.com/apps-script/reference/spreadsheet/sheet
You cannot iterate over the sheet by itself, but always iterate over the vales of range.
However you don't have to use a fixed range but can compute the range at every script execution.
To do this you could use getlastcolumn() and getLastRow().
This two methods return the last column or row with content in it.
A commodity method is getDataRange() which directly gives you the range with data in it.
Every script execution this range will extend over all your cells and columns with content.
Look at the example from Google API documentation:
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
// This represents ALL the data
var range = sheet.getDataRange();
var values = range.getValues();
// This logs the spreadsheet in CSV format with a trailing comma
for (var i = 0; i < values.length; i++) {
var row = "";
for (var j = 0; j < values[i].length; j++) {
if (values[i][j]) {
row = row + values[i][j];
}
row = row + ",";
}
Logger.log(row);
}
Hope that helps.

Unmerge in Google Apps Script

I'd like to unmerge all the cells of my google spreadsheet using script. I believe that VBA has this option (cells.unmerge) but I can't find a similar operation in GAS. I've tried this script but it didn't seem to work.
function MyFunction() {
var Sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var Range = Sheet.getDataRange().activate();
Range.clearFormat();
}
The correct word is "Range.breakApart" not "unmerge". Note that this only works when the range it is called on encompasses all merged cells.
Try this code:
function unmerge() {
var app = SpreadsheetApp;
// get current active sheet use single line coding
var activeSheet =app.getActiveSpreadsheet().getActiveSheet();
// get last row
var lstrow= activeSheet.getLastRow();
// see below description **
var mergerange = activeSheet.getRange(13,4,lstrow).getMergedRanges();
for (var i = 0; i < mergerange.length; i++) {
Logger.log(mergerange[i].getA1Notation());
Logger.log(mergerange[i].getDisplayValue());
mergerange[i].breakApart();
}
}
** 13= start row number. 4 = column number of merge cells.