Copy & Delete Function on Sheets w/ Apps Script - google-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)

Related

Moving one row to bottom of a sheet after date has passed

I am looking for a macro that can help move a row of a sheet to the bottom of it once it passes a certain date. Basically this will be used for a meeting tracker and I'm trying to find a way to automatically move meetings to a "Completed" section once the date (located on Column F) passes.
I've created macros before to move things between sheets, but I'm unfamiliar with how to move things on the same sheet. Would anyone be able to help?
Here's the sheet: https://docs.google.com/spreadsheets/d/1EPueop9bdky_J8VgpFdSUzzsMRieRUreeCRIy18ScTY/edit#gid=0
I would like to move rows based on the date in Column F. Once it passes I would like it to move to the "Completed" section of the sheet. This is an active spreadsheet so the row "Completed" it's on could change as meetings are being added.
function moveActiveRowToBottom() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getActiveSheet();
const r = sh.getActiveRange().getRow();
const vs = sh.getRange(r,1,1,sh.getLastColumn()).getValues();
sh.getRange(sh.getLastRow() + 1, 1, vs.length, vs[0].length).setValues(vs);
sh.deleteRow(r);
}
I believe your goal is as follows.
You want to check the date of column "F" of the sheet. When the date of column "F" is smaller than today, you want to move the row to the last row.
You want to achieve this in the same sheet in a Google Spreadsheet. And, the sheet has a row of "Completed" in column "A", you want to check the date of the above rows of the "Completed" row.
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. When you use this script, please run the function of myFunction().
function myFunction() {
const sheet = SpreadsheetApp.getActiveSheet(); // or const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet 1");
const row = sheet.getRange("A4:A" + sheet.getLastRow()).createTextFinder("Completed").findNext().getRow();
const now = new Date().getTime();
const moves = sheet.getRange("F4:F" + (row - 1)).getValues().reduce((ar, [f], i) => {
if (f && f.getTime() < now) {
const r = i + 4;
ar.push(sheet.getRange(`A${r}:F${r}`));
}
return ar;
}, []).reverse();
const len = moves.length;
if (len == 0) return;
const lastRow = sheet.getLastRow();
moves.forEach((r, i) => sheet.moveRows(r, lastRow - i + len - 1));
}
When this script is run, the column "F" of the rows from 4 to the "Completed" row is checked. And, when there are moving rows, the rows are moved to the last row of the sheet.
Note:
This sample script was tested using your provided Spreadsheet. When you change the Spreadsheet and/or your actual Spreadsheet is different from your provided Spreadsheet, this script might not be able to be used. Please be careful about this.
When I saw your sample Spreadsheet, it seems that the sheet name is Sheet 1. If you want to use the sheet using the sheet name, please be careful about this.
References:
reduce()
forEach()
moveRows(rowSpec, destinationIndex)

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!

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)

Automatically Add a Note Based on Dynamic Cell in Google Sheets

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();
}

search spreadsheet column for text in a string and return a result in another column

Using google apps script and spreadsheet, I have been trying to do a simple thing but can't figure out the problem. I have a sheet, with a blank column and a column with text. The columns are not next to each other. I am trying to search for text in each cell in one column, and if the text is found, then set the value of the cell in the empty column as 'Yes'.
Example (sorry no code - I've gone round and round with it for hours, and what I have is so convoluted, it's best to just provide an example):
Column A with text Empty Column F
abcd efg hij
klmn opq rstu
vwxzy Yes
What is the simplest code to search Column A for 'xyz' and return a 'Yes' in Column F?
I've looked at and tried about a dozen different code examples online, and can't get any of them work. Appreciate any help with this!!
EDIT (Final hopefully) for my use (I have some backend utilities that get me the column number based on the header name, that code not included in this, fyi):
var sskey = SpreadsheetApp.openById('**********************')
function otherfunction(){
addCustomValue('POCs', 'Groups', 'Champion', 'Champion', 'Yes');
}
function addCustomValue(sheetNamestr, searchColnamestr, writeColnamestr, searchKeystr, writeValstr) {
var sheet = sskey.getSheetByName(sheetNamestr);
var searchColnum = MyUtilities.getColIndexByName(sheet, 1, searchColnamestr);
var writeColnum = MyUtilities.getColIndexByName(sheet, 1, writeColnamestr);
var data = sheet.getDataRange().getValues();
for (n=0; n<data.length; ++n) {
if (data[n][searchColnum-1].toString().match(searchKeystr)==searchKeystr){ data[n][writeColnum-1] = writeValstr};
}
sheet.getRange(1,1,data.length,data[0].length).setValues(data);
}
Thanks Serge! Now I can run this over my spreadsheets based on any columns and conditions!
This is a possible simple script that does what you need. (If your sheet contains formulas or custom function then it should be modified to take it into account)
function test(){
var sh = SpreadsheetApp.getActiveSheet();
var data = sh.getDataRange().getValues(); // read all data in the sheet
for(n=0;n<data.length;++n){ // iterate row by row and examine data in column A
if(data[n][0].toString().match('xyz')=='xyz'){
// if column A contains 'xyz' then set value in index [5] (is column F)
data[n][5] = 'YES'
};
}
Logger.log(data)
sh.getRange(1,1,data.length,data[0].length).setValues(data); // write back to the sheet
}
function myFunction() {
//Variable to keep track of the sheet
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
//Start at row 1, end at the last row of the spreadsheet
for(var i=1;i<sheet.getLastRow();i++){
var value = sheet.getRange(i, 1).getValue();
//Compare the value of the cell to 'xyz', if it is then set column 4 for that row to "Yes"
if(value == 'xyz'){
sheet.setActiveRange(sheet.getRange(i, 4)).setValue('Yes');
}
}
}