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)
Related
I'd like to achieve the following:
Hide Columns starting from Column C up to the column that matches today's date(11/16/2022)
Script should stop hiding columns if it reaches Column Z
Apply this to 3 different sheets with same cell ranges (Sheet1, Sheet2, and Sheet3)
I want to achieve this using Google Apps Script
Appreciate your help in advance.
In your situation, how about the following sample script?
Sample script:
function myFunction() {
const sheetNames = ["Sheet1", "Sheet2",,,]; // Please set your sheet names.
sheetNames.forEach(sheetName => {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
sheet.showColumns(3, sheet.getMaxColumns() - 2);
const today = Utilities.formatDate(new Date(), Session.getScriptTimeZone(), "M/d"); // or "MM/d" or "MM/dd"
const num = sheet.getRange(3, 3, 1, sheet.getLastColumn()).getDisplayValues()[0].findIndex(e => e == today);
if (num > 0) {
sheet.hideColumns(3, num);
}
});
}
When this script is run, from your showing sample image, the cells "C3:3" are searched by the date like M/d. When today's date is found, the columns until today are hidden.
Note:
This sample script is from your showing sample Spreadsheet. So, when you change the structure of the Spreadsheet, this script might not be able to be used. Please be careful about this.
References:
forEach()
hideColumns(columnIndex, numColumns)
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!
there!
The functions below basically copies rows into another sheet. This is a tweaked sample of the code.
The issue I'm finding is:
When this copies the row(s) into the new sheet, that sheet becomes active. I'll delete it later, but the user gets dragged into this temporary sheet and I'd lie to know if there'd be a way to do it using copyTo() but without shifting tabs in front of the user.
/*
*It moves the order row into Production Sheet in WIP, as the user sets status Confirmed;
*/
function installedOnEdit(e) {
const dstSpreadsheetId = "XXXXXxxxxxxxxXXXXXXXXXX" // Please set the destination Spreadsheet ID.
const destSheetName = 'Página23'
//Maps the values to serve as criteria to decide if this should continue running
const ss = SpreadsheetApp.getActiveSpreadsheet();
const orderSheet = ss.getSheetByName('Orders');
const activeSheetName = ss.getActiveSheet().getName();
const thisRow = e.range.getRow();
const thisCol = e.range.getColumn();
const cellValue = e.range.getValue();
if (activeSheetName == orderSheet.getName() && thisRow > 5 && thisCol < 13 && cellValue == 'Confirmed') {
moveOrder(ss, orderSheet, thisRow, dstSpreadsheetId, destSheetName)
orderSheet.getRange(thisRow, thisCol).setValue('')
}
}
}
function moveOrder(ss, orderSheet, thisRow, dstSpreadsheetId, destSheetName) {
const orderHeaders = orderSheet.getRange(5, 1, 1, 13);//Gets the table headers.
const rowValues = orderSheet.getRange(thisRow, 1, 1, 13);//Gets the changed row
const newSheet = ss.insertSheet('MoveOrderToWIP');//Creates a temporary sheet
orderHeaders.copyTo(newSheet.getRange(newSheet.getLastRow() + 1, 1), { contentsOnly: true });//Moves the headers into the temporary sheet
rowValues.copyTo(newSheet.getRange(newSheet.getLastRow() + 1, 1), { contentsOnly: true });//Moves the row into the temporary sheet
}
Thank you!
When I saw your updated script, it seems that dstSpreadsheetId and destSheetName are not used in moveOrder function. Although I cannot understand whether you wanted to use only one same Spreadsheet, for example, when you want to keep the edited range as the active range even when the new sheet is inserted by insertSheet, how about the following modification?
Issue and solution:
In your script, ss is const ss = SpreadsheetApp.getActiveSpreadsheet();. In this case, when insertSheet is used, the activated ranges are changed. In this case, even when ss is used as e.source, the same result occurs. It seems that this is the current specification.
When you want to keep the active range even when insertSheet is used, please use SpreadsheetApp.openById() instead of SpreadsheetApp.getActiveSpreadsheet(). By this, the active range is not changed even when insertSheet is used.
When this is reflected in your script, it becomes as follows. In this modification, your showing script is modified. Please be careful about this.
Modified script:
From:
const newSheet = ss.insertSheet('MoveOrderToWIP');
To:
const newSheet = SpreadsheetApp.openById(ss.getId()).insertSheet('MoveOrderToWIP');
By calling the Spreadsheet from outside, even when insertSheet is used, the active range can be kept. In this case, even when Sheets API is used, the same result occurs.
Note:
In your script, after "MoveOrderToWIP" sheet was inserted, the script is run again, an error occurs. Because the same sheet name is existing in the Spredsheet. So, please be careful this.
Reference:
openById(id)
All you need is from range and to range there is no need to actually activate the either of the sheets
fromRange.copyTo(toRange);
Probably the insertSheet() is making it the active sheet. So make some other the sheet the active sheet after doing the insert
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)
I have the following script:
function onEdit(event) {
// assumes source data in sheet named Needed
// target sheet of move to named Acquired
// test column with yes/no is col 5 or E
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = event.source.getActiveSheet();
var r = event.source.getActiveRange();
if(s.getName() == "IN" && r.getColumn() == 7 && r.getValue() == "Y") {
var row = r.getRow();
var numColumns = s.getLastColumn();
var targetSheet = ss.getSheetByName("ORDERS");
var target = targetSheet.getRange(targetSheet.getLastRow() + 1, 1);
s.getRange(row, 1, 1, numColumns).copyTo(target);
}
}
This is the original post where i found the script:
https://support.google.com/docs/forum/AAAABuH1jm0hR40qh02UWE/?hl=en&gpf=%23!topic%2Fdocs%2FhR40qh02UWE
I want to make a few small adjustments to the code, but don't know where to start.
Presently, it copies the whole row when a "Y" is entered into column G and places the row contents on lastrow of ORDERS!.
What i want it to do is:
1) only copy columns B,C and E on lastrow of ORDERS!
2) delete values in E and F on IN! after code has run for that specific row (don't want it deleting rows that I haven't put a "Y" against)
3) Is there a way to have a button instead, that when the button is clicked it copies all the rows with "Y" at once?
Here's a link to my sheet if you want to have a play:
https://docs.google.com/spreadsheets/d/1Peo5_5QmkxVyL7j5bmgtMs9BL16cvsGhhOSuRV_TsAo/edit?usp=sharing
Best regards
manc
I believe your goal as follows.
When the column "G" is Y, you want to copy the values of the columns "B", "C" and "E" to the last row of the sheet ORDERS.
You want to copy all rows that the column "G" is Y when the script is run.
You want to delete the content of columns "E" and "F" of the copied rows in the sheet of IN, when the script is run.
You want to run the script by clicking a button on the sheet.
For this, how about this answer?
Modification points:
In your script,
When the script is run by clicking a button on the sheet, the event object cannot be used.
Your script copies one row of the active range.
The columns "E" and "F" of the copied rows are not deleted.
It is required to modify above modification points. When above points are reflected to the script, it becomes as follows.
Modified script:
Please copy and paste the following script to the script editor. And please prepare a button which is drawing and/or image, and assign the function run to the button. By this, when the button is clicked, the script is run. And the columns "B", "C" and "E" of all rows that the column "G" is Y are copied from the sheet "IN" to the last row of the sheet "ORDERS".
function run() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var srcSheet = ss.getSheetByName("IN");
var dstSheet = ss.getSheetByName("ORDERS");
// 1. Retrieve the values from "A2:G" of sheet "IN".
var srcValues = srcSheet.getRange("A2:G" + srcSheet.getLastRow()).getValues();
// 2. Create an object for putting values and deleting the contents of the columns "E" and "F".
var obj = srcValues.reduce((o, [,b,c,,e,,g], i) => {
if (g == "Y") {
o.values.push([b, c, e]);
o.ranges.push(`E${i + 2}:F${i + 2}`);
}
return o;
}, {values: [], ranges: []});
// 3. Copy the values to the sheet "ORDERS".
dstSheet.getRange(dstSheet.getLastRow() + 1, 1, obj.values.length, obj.values[0].length).setValues(obj.values);
// 4. Delete the contents of the columns "E" and "F" of sheet "IN".
srcSheet.getRangeList(obj.ranges).clearContent();
}
Note:
About the button for running the Google Apps Script, I think that this site is useful. Ref In your case, please create a button on the sheet "IN" and assign the function run to the button. By this, when the button is clicked, the script works.
Please use this script with V8.
References:
setValues(values)
clearContent()
reduce()