I have a simple request that combines Data Validation with VLOOKUP.
On my Pending worksheet, when you select a Website - it should create a dropdown in the next column showing only the email addresses assigned to that website within the Profiles worksheet. Lastly, when you select an Email in the newly created dropdown - the next column should show the correct Name that's assigned to that Email-Website combination.
Here's my Google Sheet:
Here is a script solution:
Script:
function onEdit(e) {
const src = e.source.getActiveSheet();
const r = e.range;
const column = r.getColumn();
const row = r.getRow();
// if non header in 'Pending' sheet is edited and the outcome has a value
if (src.getSheetName() == 'Pending' && row > 1 && e.value) {
// get data as a whole
var data = e.source.getSheetByName('Profiles').getDataRange().getValues();
// if A2:A is edited
if (column == 1) {
// filter 1st column using e.value, then return 2nd column
var emails = data.filter(row => row[0] == e.value).map(row => row[1]);
// set data (list) validation to colB
r.offset(0, 1).setDataValidation(SpreadsheetApp.newDataValidation().requireValueInList(emails).build());
// delete data in colB and colC
src.getRange(row, column + 1, 1, 2).clearContent();
}
// if B2:B is edited
else if (column == 2) {
var colA = r.offset(0, -1).getValue();
var colB = e.value;
// only populate column C if both A and B has values
if (colA && colB) {
var name = data.filter(row => row[0] == colA && row[1] == colB)[0][2];
// set value to colC
r.offset(0, 1).setValue(name);
}
}
}
}
New Data:
Modifying Data:
Related
I have columns B-E where information will be entered and column A to add the date/timestamp for when all information in columns B-E has been entered. I have got it coded where if just column B is entered but am struggling to get it for when columns B-E are entered and not just column B.
I would also like the date and time format to be MM/DD/YYYY HH:MM:SS:AM/PM
Here is the code I have so far:
function onEdit(e) {
var row = e.range.getRow();
var col = e.range.getColumn();
var targetColumn = 2;
if(targetColumn && row >1 && e.source.getActiveSheet().getName() === "HHAM"){
e.source.getActiveSheet().getRange(row,1).setValue(new Date());
}
}
Here is the document: https://docs.google.com/spreadsheets/d/1KDywZKPUpbPImnnN-X9GeVLL_VUzu16vTwEGXwCHKQ0/edit?usp=sharing
You can use Regex to check if the edited cell belongs to columns B,C,D and E.
Try this:
function onEdit(e) {
var row = e.range.getRow();
var range = e.range.getA1Notation();
if(range.match(/[B,C,D,E]\d/) && row > 1 && e.source.getActiveSheet().getName() === "HHAM"){
var data = e.range.getValues();
var filtered = data.filter(e => {
return e[0] != ''
});
if(filtered.length == 0){
e.source.getActiveSheet().getRange(row,1).setValue(""); // this will remove the date to any deletion happen to cells
}else{
e.source.getActiveSheet().getRange(row,1).setValue(new Date());
}
}
}
Sample Data:
Edit/Insert to a single cell:
Edit/Insert multiple data at once:
To change the format to MM/DD/YYYY HH:MM:SS:AM/PM:
Highlight column A
Click Format Tab -> Number -> More Formats -> Custom number format
Paste this string: mm"/"dd"/"yyyy" "hh":"mm":"ss" "AM/PM
References:
Regular Expression
I have two sheets. On one sheet (Sheet1), I have a column 'A' which has 5 fixed values, and a column 'B' which records the timestamp value in 'A' change. The other sheet (Sheet2) is meant to record all the changes in the first sheet in one day.
I use this simple query in the recording sheet:
=QUERY(Sheet1!A$1:X,"select * where C>="& Sheet2!D1)
with Sheet1!C has timestamps and Sheet2!D1 is the timestamp of 12:00 AM today
The problem is when I change the value of a row in C columns more than one time, instead of creating a new row in Sheet2 it change the value of that row in Sheet2 into new values.
So how do I change my code to get my desire results?
EDIT 2: here is my new code, but it doesn't help.
function importdata(x) {
// var row = x.range.getRow();
// var col = x.range.getColumn();
var addedsheet=SpreadsheetApp.getActive().getSheetByName("Change"); // Sheet where I want to keep the record of the change
var original_sheet = SpreadsheetApp.getActive().getSheetByName("Master"); //sheet where the change is happended
var compared_value = addedsheet.getRange(1,4).getValue(); // Cell D1 of sheet "Change", which has timestamp of today
var insert_area = original_sheet.getRange("A2:X").getValues() // area to get value from "Master" sheet to put into "Change"
var compared_area = original_sheet.getRange("C2:C").getValues(); // area where has timestamp
if (compared_area >= compared_value){
addedsheet.values.append([insert_area])}
} //if timestamp of one row from Master is greater than the value at Change!D1 =>append that row at the end (this is what I'm trying to do)
EDIT 3: I fixed the above code by append[insert_area][0] instead of [insert_area]
But then I have a new problem: there will a chance that a row in sheet 1 will be overwrited in sheet 2. I try something like this, but it returns nothing on the sheet.
function for_each_row(){
var addedsheet=SpreadsheetApp.getActive().getSheetByName("Change"); // Sheet where I want to keep the record of the change
var original_sheet = SpreadsheetApp.getActive().getSheetByName("Master"); //sheet where the change is happended
var compared_value = addedsheet.getRange(1,4).getValue(); // Cell D1 of sheet "Change", which has timestamp of today
var number_of_row_2 = addedsheet.getLastRow;
var number_of_row_1 = original_sheet.getLastRow();
for (var i=2; i<number_of_row_1 +1; i++){
var compared_stamp = original_sheet.getRange("C"+i).getValues();
var insert_values = (original_sheet.getRange(i,1,1,24).getValues())
if (compared_stamp > compared_value){
var insert_values = (original_sheet.getRange(i,1,1,24).getValues());
for (var j = 2; j<number_of_row_2 +1; j++){
var value_from_sheet = addedsheet.getRange(j,1,1,24).getValues();
if (insert_values ===value_from_sheet){
return
}
else(
addedsheet.appendRow(insert_values[0]))
}
}
}
}
My thought is if a row satisfies the 1st condition then the value will be check in sheet 2. If sheet 2 didn't have that row then append that row.
Issue:
If I understand you correctly, you want to do the following:
If sheet Master is edited, iterate through all rows in this sheet (excluding headers), and for each row, check the following:
Column C has a higher value than cell D1 in sheet Change.
This row, with these exact values, does not exist in Change.
If these conditions are meet, append the row to Change.
Solution:
Use filter and some to filter out rows that don't match your two conditions, and use setValues to write the resulting rows to your other sheet.
Code snippet:
function onEdit(e) {
var editedSheet = e ? e.range.getSheet() : SpreadsheetApp.getActiveSheet();
if (editedSheet.getName() === "Master") {
var addedSheet = SpreadsheetApp.getActive().getSheetByName("Change");
var compared_value = addedSheet.getRange(1,4).getValue();
var newData = editedSheet.getRange("A2:X" + editedSheet.getLastRow()).getValues();
var currentData = addedSheet.getRange("A2:X" + addedSheet.getLastRow()).getValues();
var filteredData = newData.filter(row => row[2] >= compared_value)
.filter(row => !currentData.some(currentRow => JSON.stringify(currentRow) === JSON.stringify(row)));
addedSheet.getRange(addedSheet.getLastRow()+1,1,filteredData.length,filteredData[0].length).setValues(filteredData);
}
}
Note:
An easy way to check if two rows have the same values is using JSON.stringify(row).
I'm assuming the timestamps are not Dates. If they are, you should compare them using getTime(). So you should change the corresponding code to newData.filter(row => row[2].getTime() >= compared_value.getTime()).
What I'm looking to do is take my google sheet that contains columns A through N. What I want to do is take the rows, except for row 1 which is a header for all the column, and have them automatically move to the bottom of the same spreadsheet based on the drop down options in column E. Once the option has been selected that signifies it's done I want the entire row to automatically move to the bottom of the spreadsheet. In addition to that I also want column D, which is the due date for the event, to update by adding one year to the date in that column as the new due date will be one year from the previous. Is this possible? Thanks for the help.
function onEdit(e) {
const row = e.range.getRow();
const col = e.range.getColumn();
const as = e.source.getActiveSheet();
if(as.getName() == "Sheet1" && col == 5 && row >1 && !as.getRange(row,col).getValue()=='MPS')
{
const row_new = as.getRange(row,1,1,col);
row_new.copyTo(as.getRange(as.getLastRow()+1,1,1,col));
as.deleteRow(row)
}
}
You should change the way you are comparing the column 5
I've used the following example:
Instead of '==' use '===' comparison because double '=' is not comparing the value but the type of the object. Having said that here's my approach:
function onEdit(e) {
const row = e.range.getRow();
const col = e.range.getColumn();
const as = e.source.getActiveSheet();
const newDate = new Date();
// No due date
if (as.getRange(row,3).getValue() > newDate) {
return;
}
if(as.getName() === "Sheet1" && col === 5 && row >1 && as.getRange(row,col).getValue()!=='MPS')
{
let row_new = as.getRange(row,1,1,col);
// Add 1 day to your due date
let oldValues = row_new.getValues();
oldValues[0][3] = new Date(oldValues[0][3].setDate(newDate.getDate() + 1));
// Set the new date
row_new.setValues(oldValues);
row_new.copyTo(as.getRange(as.getLastRow()+1,1,1,col));
as.deleteRow(row);
}
}
Where at the very beginning there's a comparison between dates in order to determine if the column D (Date) is greater than the current date or not, then in order to set the new Date I make use of the Class Date and added 1 day to the date extracted from the column D.
References
Range.getValues
Range.setValues
Ok, complete newb here...I've searched and found various 'copy row to another sheet' scripts but can only get one to work. Here is my bodged version below. What I am trying to do is have a front sheet that displays the rows from sheet 1 where one of three criteria is met i.e column N has a status of 'Edit', 'SB Edit' or 'Amend Edit'. The below does do this but it doesn't loop and each time I run it, it repeats the action and I get duplicates of all the rows on the destination sheet.
Essentially, I want it running such that any time the status of column N for a particular row is changed to one of these three values, it shows up on Sheet 2. So Sheet 2 is a 'live' display of all rows in sheet 1 where column N equals the specified value
Any help much appreciated...
function copyrange() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Sheet 1'); //source sheet
var testrange = sheet.getRange('N:N');
var testvalue = (testrange.getValues());
var csh = ss.getSheetByName('Sheet 2'); //destination sheet
var data = [];
var j =[];
//Condition check in N:N; If true copy the same row to data array
for (i=0; i<testvalue.length;i++) {
if ( testvalue[i] == 'Edit') {
data.push.apply(data,sheet.getRange(i+1,1,1,15).getValues());
//Copy matched ROW numbers to j
j.push(i);
}
}
//Copy data array to destination sheet
csh.getRange(csh.getLastRow()+1,1,data.length,data[0].length).setValues(data);
for (i=0; i<testvalue.length;i++) {
if ( testvalue[i] == 'Amend Edit') {
data.push.apply(data,sheet.getRange(i+1,1,1,15).getValues());
//Copy matched ROW numbers to j
j.push(i);
}
}
//Copy data array to destination sheet
csh.getRange(csh.getLastRow()+1,1,data.length,data[0].length).setValues(data);
for (i=0; i<testvalue.length;i++) {
if ( testvalue[i] == 'SB Edit') {
data.push.apply(data,sheet.getRange(i+1,1,1,15).getValues());
//Copy matched ROW numbers to j
j.push(i);
}
}
//Copy data array to destination sheet
csh.getRange(csh.getLastRow()+1,1,data.length,data[0].length).setValues(data);
//Delete matched rows in source sheet
//for (i=0;i<j.length;i++){
//var k = j[i]+1;
//sheet.deleteRow(k);
//Alter j to account for deleted rows
//if (!(i == j.length-1)) {
//j[i+1] = j[i+1]-i-1;
}
If you want that copy to happen when the column N is modified, it's probably best to set a trigger, like onEdit. That will give you an object with data about the event that happened, including the cell that was changed and its value.
You can then use such data to get the whole row and copy it to the other sheet, like this:
function onEdit(e) {
const statuses = ['Edit', 'SB Edit', 'Amend Edit'];
// Check if the modification happened on column N (14th column) and if the new value is one of the desired statuses
if (e.range.getColumn() === 14 && statuses.includes(e.value)) {
// Get sheets
const spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
const sheet1 = spreadsheet.getSheetByName('Sheet1');
const sheet2 = spreadsheet.getSheetByName('Sheet2');
// Get the whole row of the modified cell
const modifiedRow = sheet1.getRange(e.range.getRow(), 1, 1, sheet1.getLastColumn()).getValues();
// Append modified row to the end of Sheet2
sheet2.appendRow(modifiedRow.flat());
}
}
If you want to avoid duplicated rows in Sheet2, then you need to have a way to check whether a particular row was added to Sheet2 before.
For example, if column A has a unique identifier (ID) for rows, you can search for that ID in Sheet2 before appending a new row. If it exists, then you might want to replace it with the new values or just do nothing. If it doesn't exist, then you append the row to the end:
function onEdit(e) {
// Check if the modification happened on column N (14th column) and if the new value is one of the desired statuses
if (e.range.getColumn() === 14) {
// Specify update statuses
const addStatuses = ['Edit', 'SB Edit', 'Amend Edit'];
const removeStatuses = ['On Hold', 'Version Returned'];
// Get sheets
const spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
const sheet1 = spreadsheet.getSheetByName('Sheet1');
const sheet2 = spreadsheet.getSheetByName('Sheet2');
// Get the whole row of the modified cell
const modifiedRow = sheet1.getRange(e.range.getRow(), 1, 1, sheet1.getLastColumn()).getValues();
// Assume that ID is in column A (first column)
const modifiedRowID = modifiedRow[0][0];
// Get values from Sheet2
const columnValues = sheet2.getDataRange().getValues();
// Check if Sheet2 already has row based on the value of column A and retrieve its index
let rowIndex = -1;
for (let i = 0; i < columnValues.length; i++) {
if (columnValues[i][0] == modifiedRowID) {
rowIndex = i + 1; // JavaScript arrays indices start from 0, but Sheets row indices start from 1
break;
}
}
// If row should be added/removed to/from Sheet2
if (addStatuses.includes(e.value)) {
if (rowIndex == -1) {
// Row not found in Sheet2, so add it
sheet2.appendRow(modifiedRow.flat());
} else {
// Optional: Row found in Sheet2, so replace it with current values
sheet2.getRange(rowIndex, 1, 1, modifiedRow[0].length).setValues(modifiedRow);
}
} else if (removeStatuses.includes(e.value) && rowIndex > -1) {
// Remove row from Sheet2
sheet2.deleteRow(rowIndex);
}
}
}
I have a workbook that I am working on for our company and need a little help on this one. Normally I can figure things out pretty easy. In my worksheet there are several tabs the ones I am working with now are NEW, In Progress, On Hold, Scheduled and Complete. Permits are added to the New tab Via google form input, what I would like to happen that when the status changes in Column K is that it goes to the corresponding tab. I had this all set up in Excel but we are no longer allowed to use Excel at work. TK's Daily Production Any help would greatly be appreciated
In sheet, click Tools > Script Editor.
Paste below code. This code assumes Column K has header 'status' and this column values have corresponding tabs = sheets in Spreadsheet. As an example, if Column K has value 'NEW', there must be a tab named 'NEW'. If you edit value in this column, say from 'NEW' to 'Complete', that row will be copied to 'Complete' tab and optionally deleted from 'NEW' tab if corresponding code is activated.
// onEdit trigger
function onEdit() {
moveToTab();
}
function moveToTab() {
// active SS
var ss = SpreadsheetApp.getActiveSpreadsheet();
// active cell/range
var range = ss.getActiveRange();
// active column
var col = range.getColumn();
Logger.log(col);
// active sheet
var aSheet = ss.getActiveSheet();
// get active column header
var header = aSheet.getRange(1,col,1,1).getValue();
Logger.log(header);
// here i assume, Column K has a header 'status'
// change 'status' string to what you have as Column K header
// if column is not 'status', do nothing
if (header.toLowerCase() !== 'status') return;
// get value
var target = range.getValue();
// trim and check there is a value
if (target.toString().trim() === '') return;
// do nothing if same sheet
if (target.toString().toLowerCase() === aSheet.getName().toLowerCase()) return;
// try {
// try move
// target sheet based on Column K value
var sheets = ss.getSheets();
var tSheet = sheets.filter(function(sheet) {
if (sheet.getName().toLowerCase() == target.toLowerCase()) return true;
else return false;
})[0];
if (!tSheet) return;
// active cell row
var row = range.getRow();
// get active row values
var values = aSheet.getRange(row, 1, 1, aSheet.getLastColumn()).getValues();
// append values to target row
tSheet.appendRow(values[0]);
// optionally delete row from old sheet
// un-comment if required
aSheet.deleteRow(row);
// } catch(e) {}
}