Automatically Add a Note Based on Dynamic Cell in Google Sheets - google-apps-script

I am trying to modify a Script in Google Sheets that creates a Note that contains the content of the Cell it lives in. I think I'm almost there- however, the script I have below only references a static cell. I need it to create a note within each cell in Column C, with each note referencing the text in the specific cell it is assigned.
For example:
C1 contains "TEST", C1 Note shows "TEST"
C2 contains "HELLO", C2 Note shows "HELLO"
C3 contains "WORLD", C3 Note shows "WORLD"
Here is the script that I have currently:
function addNote() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var targetCell = sheet.getRange("C3");
var sourceCell = sheet.getRange("C3");
var noteText = sourceCell.getValue();
targetCell.setNote(noteText);
}***
Appreciate any help that can be provided- also, would be great if this could update the content of the note when the spreadsheet is updated, if anyone knows how to append that.

You want to set the values of column "C" to the notes of the same column.
The values for the notes are the same with the values of cells of column "C".
For example, when the cell "C1" has "TEST", you want to set "TEST" to the note.
When the values of column "C" in 1st tab of active Spreadsheet is edited, you want to update the notes.
You want to achieve this using Google Apps Script.
From your question and your replying comments, I could understanding like above. If my understanding is correct, how about this sample script? Please think of this as just one of several answers.
In this sample script, I used a simple trigger. By this, when the column "C" of the 1st tab is edited, the script is run and the notes of column "C" are updated using the values of column "C".
Sample script:
Please copy and paste the following script and save the script. When you edit the cell of the column "C" in the 1st tab of active Spreadsheet, the script is run and the notes are set with the values of column "C".
function onEdit(e) {
var sheet = e.source.getSheets()[0];
if (e.range.getSheet().getSheetName() === sheet.getSheetName()) {
var range = sheet.getRange("C1:C" + sheet.getLastRow());
var values = range.getValues();
range.setNotes(values);
}
}
References:
Simple Triggers
Event Objects
getValues()
setNotes(notes)

The following script will create a note using the text from the cell that is being edited. The cell must be in the third column ("C"). To change the column that can be edited, change the 3 to the correct column number (A=1,B=2,C=3,etc...)
function onEdit(e){
var range = e.range;
if (range !== 3) return;
range.setNote(e.value);
}
You may want to go a step further and clear the cell after creating the note. Here is an example of that:
function onEdit(e){
var range = e.range;
if (range.getColumn() !== 3) return;
range.setNote(e.value);
range.clearContent();
}
I have done something like this before and found it useful to be able to keep adding notes by entering values into the cell. Here's how you can do that:
function onEdit(e){
var range = e.range;
var newNote = '';
var cellValue = e.value;
if (range.getColumn() !== 3) return;
var previousNote = range.getNote() ? range.getNote() : '';
if (previousNote) {
newNote = cellValue + '\n\n' + previousNote;
} else {
newNote = cellValue;
}
range.setNote(newNote);
range.clearContent();
}

Related

Automatically populate cell in Google Sheets when another cell in same row is manually filled

In Google Sheets, I want to create a macro that will automatically populate a column in each row when another column in that row is manually filled. The autofilled cell will use a formula that takes a chunk of the information (date) that's been entered manually and use a formula to concatenate it with a random number in order to create a unique ID. After inserting and executing the formula, the macro needs to copy and then paste "values only" the result of that formula. The point is to automatically create a stable ID in response to a triggering event (entry of date in row).
In pseudocode, here's the process I'd like the macro to execute:
when (date in yyyy-mm-dd format entered into A[i]) {
fill B[i] with =CONCATENATE(SUBSTITUTE(LEFT(A[i], 7), "-", ""),RANDBETWEEN(0,1000000000));
copy B[i];
PASTE_VALUES B[i] in B[i];
}
Apologies if I've overlooked a previous answer that solves this problem. I'm not new to coding, but I am new to coding in Google Sheets and am not sure what terms or phrases to use to describe what I'm after.
I believe your goal is as follows.
For example, when a value with the format of yyyy-mm-dd is put to the cell "A1", you want to put the formula of =CONCATENATE(SUBSTITUTE(LEFT(A1, 7), "-", ""),RANDBETWEEN(0,1000000000)) to the cell "B1".
You want to fix the value of the formula as the value.
You want to achieve this using OnEdit trigger.
Added: You want to put the value to the column "B", when the column "B" is empty.
In this case, how about the following sample script?
Sample script:
Please copy and paste the following script to the script editor of Spreadsheet, and save the script. And, please set the sheet name you want to use. When you use this script, please put the value with the format of yyyy-mm-dd to the column "A", by this, the script is run.
function onEdit(e) {
const sheetName = "Sheet1"; // Please set the sheet name.
const range = e.range;
const sheet = range.getSheet();
const [a, b] = range.offset(0, 0, 1, 2).getDisplayValues()[0];
if (sheet.getSheetName() != sheetName || range.columnStart != 1 || !/\d{4}-\d{2}-\d{2}/.test(a) || b) return;
const dstRange = range.offset(0, 1);
dstRange.setFormula(`=CONCATENATE(SUBSTITUTE(LEFT(${range.getA1Notation()}, 7), "-", ""),RANDBETWEEN(0,1000000000))`);
SpreadsheetApp.flush();
dstRange.copyTo(dstRange, { contentsOnly: true });
}
Reference:
Simple Triggers
This is the script I came up with:
/** #OnlyCurrentDoc */
function onEdit(e) { //Runs every time the sheet is edited
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Sheet1'); //Change this to whatever your sheet is named
var inputCol = sheet.getRange('A1'); //Change this to whatever column the date is going to be entered
//This is the range that will be checked. Slightly redundant, but makes it easier to reuse this script without needing to retype every variable
var myRange = inputCol;
//Get the row & column indexes of the active cell
var row = e.range.getRow();
var col = e.range.getColumn();
//Check that your edited cell is within the correct column
if (col == myRange.getColumn()) { //This runs only if a value is entered into the column defined in 'inputCol'
if(sheet.getRange(e.range.getA1Notation()).getValue() == '') {return}; //If the edited cell is empty (ie the date is deleted, nothing happens)
if(row == 1) {return} //If the header is changed, nothing happens
let codeCell = sheet.getRange('B'+row); //Change to the column that will store the generated code value
codeCell.setValue('=CONCATENATE(SUBSTITUTE(LEFT(A'+row+', 7), "-", ""),RANDBETWEEN(0,1000000000))');
let hardValue = codeCell.getValue(); //Gets the value from the formula you just entered
codeCell.setValue(hardValue); //Replaces the formula with just the resulting value
};
}
Comments are included to explain everything that is happening. Linked below is the spreadsheet I used to test this. It is set to allow editing, so feel free to use it to test the script yourself.
https://docs.google.com/spreadsheets/d/1UONgRPBEbxn8CQeiRSPS4eFKHjh4ae8hXGYn6ImHxeI/edit?usp=sharing
Hope this helps!

Copy & Delete Function on Sheets w/ Apps Script

After someone submits a Google Form response, their responses go to the DropRequests sheet (although the form is not currently linked for reasons).
What we want to do is after someone submits their form, and their responses go to the DropRequests sheet, that if the value in column C matches that of column F in the StudentMatches sheet, it is moved to the OldMatches sheet. We have started on the code below but it does not yet work. Any ideas on how to make this functional and fix the issue with the range in the last line?
function moveMatch(){
var oldmatches = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("OldMatches");
var droprequest = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("DropRequests");
var currentmatches = SpreadsheetApp.openById('1dd9UhD2LpshCFVYizpf3OwI1XzPrq3AfqhMAO1iJ6Ns')
value1 = currentmatches.getRange("F:F").getDisplayValues();
value2 = droprequest.getRange("C:C").getDisplayValues();
for(var i in value1)
if(value2[0,i]=value1){
currentmatches.getDataRange.getRow(0,i).moveTo(oldmatches.getLastRow())
}
}
Thank you again.
I believe your goal is as follows.
There are 3 sheets DropRequests, StudentMatches, OldMatches in your Spreadsheet.
You want to retrieve the values of column "C" from DropRequests sheet, and want to compare these values with the values of column "F" of StudentMatches sheets.
When the values are matched, you want to move the row from the StudentMatches sheet to the 1st empty row of OldMatches sheet.
When moveTo is used, the moved row becomes the empty row. In your goal, you want this situation.
In this case, how about the following sample script?
Sample script:
function moveMatch() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var srcSheet1 = ss.getSheetByName("DropRequests");
var srcSheet2 = ss.getSheetByName("StudentMatches");
var dstSheet = ss.getSheetByName("OldMatches");
var srcValues1 = srcSheet1.getRange("C2:C" + srcSheet1.getLastRow()).getValues().map(([c]) => c);
var [,...srcValues2] = srcSheet2.getDataRange().getValues();
var values = srcValues2.flatMap((r, i) => srcValues1.includes(r[5]) ? i + 2 : []);
if (values.length == 0) return;
var lastCol = srcSheet2.getLastColumn();
var lastRow = dstSheet.getLastRow();
values.forEach((r, i) => srcSheet2.getRange(r, 1, 1, lastCol).moveTo(dstSheet.getRange(lastRow + 1 + i, 1)));
}
In this modification, the values are retrieved from the column "C" of "DropRequests" and "StudentMatches". And, the values of the column "C" of "DropRequests" are compared with the column "F" of "StudentMatches" sheet. When the values are matched, the row is moved from "StudentMatches" to "OldMatches".
Note:
First, please check the sheet names, again.
This sample script is for your question. So, when the Spreadsheet is changed, this script might not be able to be used. Please be careful this.
References:
forEach()
moveTo(target)

On edit of a row in google sheet execute a formula on the data of set row and paste the result of the formula in the first column of set row

I am making a database of items. As items will be edited by many people I would like formulas to be hidden so people would not manipulate them easily compromising the database.
I tried to edit a script that recognizes any edit within a row and execute a formula on column values of set row pasting the result of the formula in column A of set row.
As I am very new to google app script I don't know how to proceed and many examples I have looked through don't do what I want.
I hope what I have written is clear.
Thanks in advance for any help!
Here is the code I wrote:
function onEdit(e) {
// Your sheet params
var sheetName = "Sheet1";
var ModifiedColumnIndex = 1;
var ModifiedColumnLetter = 'A';
var range = e.range; // range just edited
var sheet = range.getSheet();
if (sheet.getName() !== sheetName) {
return;
}
// If the column isn't our source column
if (range.getColumn() != ModifiedColumnIndex) {
var row = range.getRow();
// Formula to be executed on the edited rows cells
var formula = sheet + row [lower[trim(B&" "&C&" "&D&" "&E&" "&F&" "&G&" ["&I&"]"]])
var ModifiedRange = sheet.getRange(ModifiedColumnLetter + row.toString());
//Inserting formula into column A
ModifiedRange.setValue(formula);
};
};

How to get all old values for a mulitple cell range with onEdit trigger

In google apps script, with onEdit trigger, how do I get the old values of all the cells if the edited range has multiple cells? In the Event object documentation https://developers.google.com/apps-script/guides/triggers/events it is specified that the oldValue attribute is "Only available if the edited range is a single cell".
In my case, I use onEdit from the Event object to run a function (that needs oldValue and newValue) only when a specific column is edited. It works fine when the user selects only one cell in my specific column, but if the user selects a few cells or the entire row for example, only data from the first selected cell is retrieved but I need to access the oldValue of my specific column.
You want to retrieve old values when the multiple cells are edited.
If my understanding is correct, how about this answer?
Issue:
Unfortunately, in the current stage, when the multiple cells are edited, there are no methods for retrieving all old values from the event object.
Workaround:
So as the current workaround, how about this flow?
Copy the active Spreadsheet.
This is run only one time.
When the cells are edited, the old values are retrieved by comparing the active Spreadsheet and copied Spreadsheet.
Update the copied Spreadsheet.
By above flow, the cycle for retrieving old values when the cells are edited can be created. When this flow is reflected to the script, it becomes as follows. Please think of this as just one of several answers.
Sample script:
When you use this script, please install the OnEdit event trigger to the function of onEditByTrigger() after copy and paste this script to the script editor of the container-bound script. By this, when the cells are edited, you can see the current and old values at the log.
var backupfilename = "backupfile";
function copyToo(srcrange, dstrange) {
var dstSS = dstrange.getSheet().getParent();
var copiedsheet = srcrange.getSheet().copyTo(dstSS);
copiedsheet.getRange(srcrange.getA1Notation()).copyTo(dstrange);
dstSS.deleteSheet(copiedsheet);
}
// This is run only one time.
function init() {
// Source
var srcss = SpreadsheetApp.getActiveSheet();
var range = srcss.getDataRange().getA1Notation();
var srcrange = srcss.getRange(range);
var srcsheetname = srcss.getName();
// Destination
var backupfile = DriveApp.getFilesByName(backupfilename);
var dstid = backupfile.hasNext()
? backupfile.next().getId()
: SpreadsheetApp.create(backupfilename).getId();
var dstss = SpreadsheetApp.openById(dstid).getSheets()[0]
var dstrange = dstss.getRange(range);
dstss.setName(srcsheetname);
copyToo(srcrange, dstrange);
PropertiesService.getScriptProperties().setProperty('backupfileid', dstid);
return dstid;
}
function onEditByTrigger(e) {
var columnNumber = 1; // If you want to retrieve the old values when the column "A" is edited, it's 1.
var source = e.source;
var range = e.range;
var dstid = PropertiesService.getScriptProperties().getProperty('backupfileid');
if (!dstid) {
dstid = init();
}
if (e.range.columnStart == columnNumber) {
var range = source.getSheetName() + "!" + range.getA1Notation();
var fields = "sheets(data(rowData(values(formattedValue,userEnteredFormat,userEnteredValue))))";
var currentValue = source.getRange(range).getValues();
var oldValue = SpreadsheetApp.openById(dstid).getRange(range).getValues();
Logger.log("currentValue %s", currentValue)
Logger.log("oldValue %s", oldValue)
}
// Update backup file
var range = e.source.getDataRange().getA1Notation();
var srcrange = e.source.getRange(range);
var dstrange = SpreadsheetApp.openById(dstid).getSheets()[0].getRange(range);
copyToo(srcrange, dstrange);
}
Note:
This is a sample script for retrieving the old values when the multiple cells were edited. So if you use this script, please modify this to your situation.
References:
Event Objects
Installable Triggers
If this was not the direction you want, I apologize.

How to add an input parameter to onEdit Function

I'm trying to create a custom onEdit function with google sheets script editor. I want to know if its possible to add an input parameter such as onEdit(e,row) where row is an integer I use to specify a target cell. Here is my noob code:
function onEdit(e,row) {
// writes the current date to the cell in column B on the same row when a cell in a specific column is edited
var sheetNameToWatch = "M2";
var columnNumberToWatch = /* column */ 7; // column A = 1, B = 2, etc.
var ss = SpreadsheetApp.getActiveSpreadsheet(); // not used atm
var sheet = SpreadsheetApp.getActiveSheet(); // sheet on which changes are tracked
var sheet2 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("GRID-TRACKING") // sheet that contains formula cells
var range = sheet.getActiveCell(); // active cell is cell being edited
if (sheet.getName() == sheetNameToWatch && range.getColumn() == columnNumberToWatch) {
var targetCell = sheet2.getRange(row, 2); // I want the input parameter to control the "row" so I can autofill
targetCell.setValue("" + Utilities.formatDate(new Date(), "GMT", "yyyy-MM-dd")); // writes current date to cell
}
}
I'm using this to write the latest date of any edit on sheet "M2" column G to the sheet with formulas("GRID-TRACKING"). I put the formulas in column A and write the dates to column B. The problem is that my "row" variable is undefined...if I replace it with a fixed number and dont try to add the parameter at all it works fine, but I need a parameter I can autofill.
in the cell my formula is =onedit(cell,row)
Thanks,
Umpsy
Short answer
If you want to use two or more arguments use another name instead of onEdit for your function.
Explanation
On Google Apps Script, onEdit is a reserved function name . If you don't follow the guidelines to use the reserved function names you could get unexpected results.
References
https://developers.google.com/apps-script/guides/triggers/
You cannot pass any other variables to onEdit. However, if you are using this as a formula, I don't really see why you would even need an onEdit trigger, it can just as successfully be thisFunc(). As I see in the code, you do not even use the event object so there is definitely no reason to have an onEdit. You also have to consider that it will start any time you edit any cell on the spreadsheet.