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()).
Related
Is it possible for my Google sheet to automatically append a new formatted row to the end of a column when it fills up?
I would like to avoid having to manually create, say, 1000 pre-formatted empty rows in my sheet.
This is possible using Google Apps Script onEdit() trigger.
Try:
function onEdit(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var range = sheet.getRange(1, 1, sheet.getLastRow(), 3); //Range is set from COL A - COL C up to last row with data
var lastRow = sheet.getLastRow();
var val = range.getValues();
var lastVal = val[val.length - 1];
//Check if the edit happened within Columns A-C
var range = e.range
var col = range.getColumn();
//Set sheetname and cell range where the options are coming from
var options = ss.getSheetByName("Names").getRange("A2:A")
var dropdownCell1 = sheet.getRange(lastRow + 1, 1);
var dropdownCell2 = sheet.getRange(lastRow + 1, 2);
if (col>= 1 && col <= 3 && !lastVal.includes('')) { //checks if the last row of data is filled
//Sets the data validation rule to the cells
var rule = SpreadsheetApp.newDataValidation().requireValueInRange(options)
dropdownCell1.setDataValidation(rule)
dropdownCell2.setDataValidation(rule)
}
}
Result:
Explanation:
This uses onEdit() trigger which runs every time you make edit to a cell value. This then checks if the edit happens within Columns A-C and checks if the last row from Columns A-C are filled. It then adds the data validation rule to the cells. The dropdown comes from a sheet called 'Names' and the options are listed there.
I just did not add a code to reverse it if a data is deleted. You may edit the range if you are using different columns. Hope this helps!
Format the sheet down to the last row, as tested here in this GIF using Sequence(20), Sequence(2000).
first time posting and new at all of this, so I apologize in advance.
I have 2 sheets, "Origin" sheet and "Destination" sheet.
In the Origin sheet, if "APPROVED" is typed and entered in column C, then I would like:
the text in the cells from column A of that row to go to the first blank row of column C in Destination sheet AND
-for the text in the cells from column B of that row to go to the first blank row of column B in Destination sheet.
I have only been able to find the code below that works to copy the entire row. I would like to retain all of the function of deleting the row in the Origin sheet once it is copied.
Thanks in advance!
[Code
function onEdit(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Origin'); //source sheet
var testrange = sheet.getRange('C:C');
var testvalue = (testrange.getValues());
var csh = ss.getSheetByName('Destination'); //destination sheet
var data = [];
var j =[];
//Condition check in G:G; If true copy the same row to data array
for (i=0; i<testvalue.length;i++) {
if ( testvalue[i] == 'APPROVED') {
data.push.apply(data,sheet.getRange(i+1,1,1,11).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;
}
}
}
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 am trying to do a SUM of last 12 rows in the column (I'll be adding more rows into this column so I wanted to automate the calculation).
First of all, I am able to get the value of last cell with some value in this column by =SUMPRODUCT(MAX((B1:B200<>"")*ROW(B1:B200))) - result is stored in C1. However, I am not sure how to use this value inside the SUM formula, I was thinking something like =SUM(B(get value of C1)-12:B(get value of C1).
I tried multiple things but none of them have worked - I also don't mind using a different approach if it gets the job done.
You can create your own custom function to do that using Google Apps Script (GAS).
Try the following:
function onEdit(e){
var row = e.range.getRow();
var col = e.range.getColumn();
if ( col==2 && e.source.getActiveSheet().getName() == "Sheet1" ){
e.source.getActiveSheet().getRange("C1").setValue(sumLast12());
}
}
function sumLast12() {
var sheet = SpreadsheetApp.getActive().getSheetByName("Sheet1");
var sheet_size = sheet.getLastRow();
var elmt = sheet.getRange("B1:B"+sheet_size).getValues().flat([1]);
var elmt12 = elmt.slice(-12);
var sum = 0;
for( var i = 0; i < elmt12.length; i++ ){
sum += parseInt( elmt12[i], 10 );
}
return sum;
}
Explanation:
In order to activate this functionality go the menu bar on top of the
spreadsheet file and click on Tools => Script editor and copy the
aforementioned code into a blank script document (see attached
screenshot for more information) and save the document (cntrl+s).
After the script has been saved, everytime you edit a cell in column
B (either by adding a new value on the bottom or modify an existing value, the script will automatically update the value in
cell C1 with the sum of the last 12 values in column B.
Note that if you don't want to change my code, name the sheet you are working with as Sheet1.
Does this work?
=SUM(FILTER(B:B,ROW(B:B)>=MAX(ROW(B:B))-12)
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) {}
}