I copy "range" from one sheet to another using range.copyTo(..) function, it doesnt copy group information
var destSheet = some_sheet;
var sourceRange=SpreadsheetApp.getActiveSheet().getRange("A1:H10");
sourceRange.copyTo(destSheet.getRange(1,1));
Values, format, formula are copied. Not groups
Issue and workaround:
In the current stage, it seems that the dimension groups cannot be directly copied by copyTo method. So, in order to copy the dimension groups, it is required to retrieve the dimension groups and put them.
When Sheets API is used, the dimension groups can be retrieved as an object. Using this, in this answer, I would like to propose to use Sheets API. When this is reflected in your script, it becomes as follows.
Modified script:
Before you use this script, please enable Sheets API at Advanced Google services. And, please set your destination sheet name.
function deleteDimensionGroups_(sheetId, sheet) {
var obj = [...(sheet.rowGroups || []), ...(sheet.columnGroups || [])];
if (obj.length == 0) return [];
return obj
.sort((a, b) => a.depth < b.depth ? 1 : -1)
.map(o => {
o.range.sheetId = sheetId;
delete o.depth;
return { deleteDimensionGroup: o };
});
}
// Please run this function.
function main() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var ssId = ss.getId();
var srcSheet = ss.getActiveSheet();
var dstSheet = ss.getSheetByName("Sheet2"); // Please set your destination sheet name.
var sourceRange = srcSheet.getRange("A1:H10");
sourceRange.copyTo(dstSheet.getRange(1, 1));
// Copy dimension groups.
var { sheets } = Sheets.Spreadsheets.get(ssId, { ranges: [srcSheet.getSheetName(), dstSheet.getSheetName()] });
var sheet = sheets[0];
var obj = [...(sheet.rowGroups || []), ...(sheet.columnGroups || [])];
if (obj.length == 0) return;
var sheetId = dstSheet.getSheetId();
var reqs = deleteDimensionGroups_(sheetId, sheets[1]);
var requests = [...reqs, ...obj
.sort((a, b) => a.depth > b.depth ? 1 : -1)
.map(o => {
o.range.sheetId = sheetId;
delete o.depth;
return { addDimensionGroup: o };
})];
Sheets.Spreadsheets.batchUpdate({ requests }, ssId);
}
Note:
In this sample script, the existing dimension groups are deleted from the destination sheet and the new dimension groups are copied. If you don't want to delete the dimension groups in the destination sheet, please modify the above script as follows.
From:
var reqs = deleteDimensionGroups_(sheetId, sheets[1]);
To:
var reqs = [];
References:
Method: spreadsheets.batchUpdate
AddDimensionGroupRequest
Added:
From your following reply,
It's full of informations. Trying to adapt it and create a copyToWithGroup(sourceRange, destRange).. not so easy at my level. But I have materials in your answer to work with.
If you want to limit the range using var sourceRange = srcSheet.getRange("A1:H10");, how about the following sample script?
Sample script:
function deleteDimensionGroups_(sheetId, obj) {
if (obj.length == 0) return [];
return obj
.sort((a, b) => a.depth < b.depth ? 1 : -1)
.map(o => {
o.range.sheetId = sheetId;
delete o.depth;
return { deleteDimensionGroup: o };
});
}
// Please run this function.
function main() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var ssId = ss.getId();
var srcSheet = ss.getActiveSheet();
var dstSheet = ss.getSheetByName("Sheet2"); // Please set your destination sheet name.
var sourceRange = srcSheet.getRange("A1:H10"); // In this sample, this range is used.
sourceRange.copyTo(dstSheet.getRange(1, 1));
// Copy dimension groups in "sourceRange".
var startRowIndex = sourceRange.getRow() - 1;
var endRowIndex = startRowIndex + sourceRange.getNumRows();
var startColumnIndex = sourceRange.getColumn() - 1;
var endColumnIndex = startColumnIndex + sourceRange.getNumColumns();
var { sheets: [src, dst] } = Sheets.Spreadsheets.get(ssId, { ranges: [srcSheet.getSheetName(), dstSheet.getSheetName()] });
var check = f => f.filter(({ range }) => {
if (range.dimension == "ROWS" && range.startIndex >= startRowIndex && range.endIndex <= endRowIndex) {
return true;
} else if (range.dimension == "COLUMNS" && range.startIndex >= startColumnIndex && range.endIndex <= endColumnIndex) {
return true;
} else {
return false;
}
});
var [src2, dst2] = [src || [], dst || []].map(e => {
var temp = [];
if (e.rowGroups && e.rowGroups.length > 0) temp = [...temp, ...check(e.rowGroups)];
if (e.columnGroups && e.columnGroups.length > 0) temp = [...temp, ...check(e.columnGroups)];
return temp;
});
if (src2.length == 0) return;
var sheetId = dstSheet.getSheetId();
var reqs = deleteDimensionGroups_(sheetId, dst2);
var requests = [...reqs, ...src2
.sort((a, b) => a.depth > b.depth ? 1 : -1)
.map(o => {
o.range.sheetId = sheetId;
delete o.depth;
return { addDimensionGroup: o };
})];
Sheets.Spreadsheets.batchUpdate({ requests }, ssId);
}
Related
I have 1 spreadsheet with multiple sheets.
The 1st and the 2nd sheets sometimes have similar data in the rows (duplicates). Also, each sheet has a column (8) with a checkbox.
Task: I need to move one of the duplicates to the 3rd sheet when the checkboxes in both sheets (1st and 2nd) are checked.
Here is the code that moves the row when it's checked in a single sheet.
Can someone help me modify it to complete my task?
function onEdit(e) {
let range = e.range;
let col = range.getColumn();
let row = range.getRow();
let val = range.getValue();
let source = e.source.getActiveSheet();
let ss = SpreadsheetApp.getActiveSpreadsheet();
let sourceSheet = ss.getSheetByName(source.getName());
if (sourceSheet.getName() == 'SHEET1' && col == 8 && val == true){
let data = sourceSheet.getRange(row,1,1,7).getValues();
let targetSheet = ss.getSheetByName('SHEET2');
targetSheet.appendRow(data[0]);
sourceSheet.deleteRow(row);
}
if (sourceSheet.getName() == 'SHEET2' && col == 8 && val == true){
let data = sourceSheet.getRange(row,1,1,7).getValues();
let targetSheet = ss.getSheetByName('SHEET3');
targetSheet.appendRow(data[0]);
sourceSheet.deleteRow(row);
}
}
Sample Spreadsheet
Apply filter()
You can add the filter() method (combined with the JSON.stringify() method) to your script to determine if a row is a duplicate of another from the other sheet. The modified script should somehow look like this:
function onEdit(e) {
var range = e.range;
var col = range.getColumn();
var row = range.getRow();
var val = range.getValue();
var source = e.source.getActiveSheet();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sourceSheet = ss.getSheetByName(source.getName());
var targetSheet = ss.getSheetByName('Sheet3');
//If Sheet1 checkbox is triggered
if (sourceSheet.getName() == 'Sheet1' && col == 8 && val == true) {
var rowArr = ss.getSheetByName('Sheet1').getRange(row, 1, 1, 8).getValues();
var lastRow2 = ss.getSheetByName('Sheet2').getLastRow();
var sheet2Values = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet2').getRange(1, 1, lastRow2, 8).getValues();
var count = sheet2Values.filter(x => {
return (JSON.stringify(x) === JSON.stringify(rowArr[0]))
});
var otherRow = sheet2Values.map((x, i) => {
if (JSON.stringify(x) === JSON.stringify(rowArr[0])) {
return i;
}
});
if (count.length > 0) {
var otherRow = sheet2Values.map((x, i) => {
if (JSON.stringify(x) === JSON.stringify(rowArr[0])) {
return i;
}
}).filter(y => y);
rowArr[0].pop();
targetSheet.appendRow(rowArr[0]);
sourceSheet.deleteRow(row);
var otherSheet = ss.getSheetByName('Sheet2');
otherSheet.deleteRow(otherRow[0] + 1)
}
}
//If Sheet2 checkbox is triggered
if (sourceSheet.getName() == 'Sheet2' && col == 8 && val == true) {
var rowArr = ss.getSheetByName('Sheet2').getRange(row, 1, 1, 8).getValues();
var lastRow = ss.getSheetByName('Sheet1').getLastRow();
var sheet1Values = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1').getRange(1, 1, lastRow, 8).getValues();
var count = sheet1Values.filter(x => {
return (JSON.stringify(x) === JSON.stringify(rowArr[0]))
});
if (count.length > 0) {
var otherRow = sheet1Values.map((x, i) => {
if (JSON.stringify(x) === JSON.stringify(rowArr[0])) {
return i;
}
}).filter(y => y);
rowArr[0].pop();
targetSheet.appendRow(rowArr[0]);
sourceSheet.deleteRow(row);
var otherSheet = ss.getSheetByName('Sheet1');
otherSheet.deleteRow(otherRow[0] + 1)
}
}
}
Output
Based on your sample spreadsheet, the modified script should work as shown below:
Please take note that the appendRow() method appends any row to the last non-empty row in the sheet. A row with a checkbox is not considered to be an empty row thus explains the behavior of the output.
Reference
filter()
JSON.stringify()
Would that do what you want ?
function copyDuplicateRows() {
var spreadsheet = SpreadsheetApp.getActive();
var sheet1 = spreadsheet.getSheetByName("Sheet1");
var sheet2 = spreadsheet.getSheetByName("Sheet2");
var sheet3 = spreadsheet.getSheetByName("Sheet3"); // destination sheet
var data1 = sheet1.getDataRange().getValues();
var data2 = sheet2.getDataRange().getValues();
// Use a JavaScript object to keep track of duplicate rows
var seen = {};
// Iterate through data1 and copy unique rows to sheet3
for (var i = 0; i < data1.length; i++) {
var row = data1[i];
var key = row.join(); // create a unique key for each row
if (!seen[key]) {
seen[key] = true;
sheet3.appendRow(row);
}
}
// Iterate through data2 and copy unique rows to sheet3
for (var i = 0; i < data2.length; i++) {
var row = data2[i];
var key = row.join();
if (!seen[key]) {
seen[key] = true;
sheet3.appendRow(row);
}
}
}
I have two sheets, source (Staff) and destination (PatternToApply). In source I have dynamic list of people (it's getting updated automatically from another spreadsheet) and in destination I have static list of people.
I am trying to workout a script which automatically compare the list from both sheets and...
if there is a new name in source sheet copy to destination sheet and insert a row below
if in source sheet name is missing but it is in the destination sheet delete row from destination sheet
I'm still learning and don't know what I'm doing wrong. Please, any help will be much appreciated.
Here's the test spreadsheet
https://docs.google.com/spreadsheets/d/1mxmnsLeQFlorHj-N8MJX-Tue0v6zTKw4fUDhjZ1bGqs/edit?usp=sharing
Thank you!
Here's what I have so far
function UpdateAgList() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh1 = ss.getSheetByName('Staff');
var sh2 = ss.getSheetByName('PatternToApply');
var data1 = sh1.getRange(1,1,sh1.getLastRow(),1).getValues();
var data2 = sh2.getRange(1,1,sh1.getLastRow(),1).getValues();
for(i = 1; i<data1.length; i++){
if(data1[i] == data2[i]){
}
else if(data1[i] != data2[i] && data2[i] == null){
var numColumns = data1.getLastColumn();
var rows = data1.getRow();
var rowt = data2.getRow();
var target = sh2.getRange(rowt, 1);
data1.getRange(rows, 1, 1, numColumns).copyTo(target);
sh2.insertRowAfter();
}
else if(data1[i] != data2[i] && data1[i] == null){
target.clearContent();
}
}
}
Modification points:
I thought that sh1.getLastRow() of var data2 = sh2.getRange(1,1,sh1.getLastRow(),1).getValues(); should be sh2.getLastRow().
In your script, copyTo, getRange, and clearContent are used in a loop. In this case, the process cost will become high.
When these points are reflected in your script, how about the following modification?
Modified script:
function UpdateAgList() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh1 = ss.getSheetByName('Staff');
var sh2 = ss.getSheetByName('PatternToApply');
var data1 = sh1.getRange(1, 1, sh1.getLastRow(), 1).getValues();
var targetRange = sh2.getRange(1, 1, sh2.getLastRow(), 1);
var data2 = targetRange.getValues();
var srcAr = data1.map(([a]) => a);
var dstAr = data2.map(([a]) => a);
var values = [
...dstAr.map(a => [a.toString() == "" ? a : (!srcAr.includes(a) ? "" : a)]),
...srcAr.reduce((ar, a) => (!dstAr.includes(a) && ar.push([a]), ar), []),
];
targetRange.clearContent();
sh2.getRange(1, 1, values.length).setValues(values);
}
When this script is run, your goal is achieved while the empty rows are kept at the destination sheet.
If you want to remove the empty rows at the destination sheet, please modify the above script as follows.
From
var values = [
...dstAr.map(a => [a.toString() == "" ? a : (!srcAr.includes(a) ? "" : a)]),
...srcAr.reduce((ar, a) => (!dstAr.includes(a) && ar.push([a]), ar), []),
];
To
var values = [
...dstAr.map(a => [a.toString() == "" ? a : (!srcAr.includes(a) ? "" : a)]),
...srcAr.reduce((ar, a) => (!dstAr.includes(a) && ar.push([a]), ar), []),
].filter(String);
References:
map()
reduce()
I want to split the google sheet into different workbooks, not tabs in the same workbook based on values in column A. Although I have got a script that splits the data into different workbooks but the data range in it is not dynamic like the number of columns to be added into each workbook are fixed. I want them to be dynamic like till the last column of the data range. I have tried a lot to make it dynamic by adding loops but it shows The number of columns in the data does not match the number of columns in the range. The data has 1 but the range has 12. this error. The data in the log has almost no difference for the fixed range (which is working fine) and for the dynamic range that I have tried to it But don't know why it is showing error. Have got stuck into it. any help will be highly appreciated.
This the function that I am trying.
function splitSheets() {
var theWorkbook = SpreadsheetApp.getActiveSpreadsheet();
var theSheet = theWorkbook.getSheetByName("Master");
var slc = theSheet.getDataRange().getLastColumn()
var slcv = theSheet.getRange("B1:B" + slc).getValues()
var sheets = theWorkbook.getSheets();
for (i = 0; i < sheets.length; i++) {
switch(sheets[i].getSheetName()) {
case "Master":
break;
default:
theWorkbook.deleteSheet(sheets[i]);
}
}
var key = theSheet.getRange("A:A").getValues();
var rows = theSheet.getDataRange().getValues();
var headerFormat = theSheet.getRange("2:2").getValues();
var folderId = '16XVypjB5_PWe2PaBIREpDGCNQlZuWL4k'
var completedSheets = [];
for (var i = 2; i < key.length; i++) {
// if(completedSheets.includes('Blank') && key[i][0] === ""){
// }else{
if(!completedSheets.includes(key[i][0]) ) {
if (key[i][0] === "") {
var name = 'Blank'
var resource = {
title: name,
mimeType: MimeType.GOOGLE_SHEETS,
parents: [{ id: folderId }]
}
var insertedFile = Drive.Files.insert(resource)
var csid = insertedFile.id
var currentSheet = SpreadsheetApp.openById(csid).getSheetByName("Sheet1")
// var currentSheet = theWorkbook.insertSheet("Blank");
} else {
var name = key[i][0]
var resource = {
title: name,
mimeType: MimeType.GOOGLE_SHEETS,
parents: [{ id: folderId }]
}
var insertedFile = Drive.Files.insert(resource)
var csid = insertedFile.id
var currentSheet = SpreadsheetApp.openById(csid).getSheetByName("Sheet1")
// var currentSheet = theWorkbook.insertSheet(key[i][0]);
}
var theNewRows =[];
var b=0;
for(var j = 1; j < rows.length; j++) {
var rown = []
for(var c = 0; c < slcv.length; c++){
// some other trials
// if((rows[j][0] == key[i][0]) || (rows[j][0] === '' && currentSheet.getName() == "Blank")){
// theNewRows[b]=[];
// theNewRows[b].push (
// rows[j][c].toString()
// This although adds the data and range dynamically but also shows the mentioned error.
rown.push(rows[j][c])
// );
// b++;
// }
}
if((rows[j][0] == key[i][0]) || (rows[j][0] === '' && currentSheet.getName() == "Blank")){
theNewRows[b]=[];
theNewRows[b].push (
rown.toLocaleString()
// These are the fixed column for data rnage
// rows[j][0],rows[j][1],rows[j][2],rows[j][3],rows[j][4],rows[j][5],rows[j][6],rows[j][7],rows[j][8],rows[j][9],rows[j][10],rows[j][11]
);
b++;
}
Logger.log(rown)
}
Logger.log(theNewRows)
// Logger.log(theNewRows)
currentSheet.getRange("1:1").setValues(headerFormat)
var outrng = currentSheet.getRange(2,1,theNewRows.length, slc);//Make the output range the same size as the output array
outrng.setValues(theNewRows);
currentSheet.autoResizeColumns(1, slc);
if(currentSheet.getSheetName() == 'Blank') {
completedSheets.push('Blank');
last = "Blank";
}else{
completedSheets.push(key[i][0])
last = key[i][0]
// }
}
}
}
SpreadsheetApp.setActiveSheet(theWorkbook.getSheetByName('Master'));
}
I overhauled and improved your script to be more readable and use a lot less Spreadsheet calls by using array methods instead.
Script:
function splitSheets() {
var folderId = '*** FOLDER ID ***';
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheets = spreadsheet.getSheets();
var sheet = spreadsheet.getSheetByName('Master');
// delete sheets that are not named 'Master'
sheets.forEach(sheetIter => {
if(sheetIter.getSheetName() != 'Master')
spreadsheet.deleteSheet(sheetIter);
});
var data = sheet.getDataRange().getValues();
// remove 1st row (blank row)
data.shift();
// remove 2nd row from data and assign as headers
var headers = data.shift();
// get unique list of sheet names from column A
var sheetNames = data.map(row => row[0]).filter(onlyUnique);
// loop those unique sheetNames
sheetNames.map(sheetName => {
// filter data by getting only rows with same column A and sheetName
var outputData = data.filter(row => row[0] == sheetName);
// add header from data filtered
outputData.unshift(headers);
var resource = {
title: sheetName || 'Blank',
mimeType: MimeType.GOOGLE_SHEETS,
parents: [{ id: folderId }]
}
var file = Drive.Files.insert(resource);
var currentSheet = SpreadsheetApp.openById(file.id).getSheetByName('Sheet1');
// write data filtered with the header
currentSheet.getRange(1, 1, outputData.length, outputData[0].length).setValues(outputData);
// resize the columns
currentSheet.autoResizeColumns(1, outputData[0].length);
});
}
// function to get unique values from array using filter
function onlyUnique(value, index, self) {
return self.indexOf(value) === index;
}
Sample Output:
I would like to find if a certain value is in a range using app scripts for google sheets.
var sheet = SpreadsheetApp.getActiveSheet();
var rangeBikeNumbers = sheet.getDataRange("A5:A5000");
var values = rangeBikeNumbers.getValues();
If I have my range rangeBikeNumbers, how can I check if the number "42" for example is in that range. I have searched for hours now and have beeb unable to find any answer to this. indexOf only seems to return -1, regardless of whether or not the value is in the range.
var indexDataNumber = values.indexOf(42); for example always ends up being -1
I believe your goal as follows.
You want to check whether the value of 42 is existing in the range of A5:A5000.
In this case, I would like to propose to use TextFinder. Because when TexiFinder is used, the process cost is low. Ref By the way, getDataRange has not arguments. From your script, I thought that you might want var rangeBikeNumbers = sheet.getRange("A5:A5000");.
When this is reflected to your script, it becomes as follows.
Modified script:
function myFunction() {
var sheet = SpreadsheetApp.getActiveSheet();
var rangeBikeNumbers = sheet.getRange("A5:A5000");
var find = rangeBikeNumbers.createTextFinder("42").matchEntireCell(true).findNext();
if (find) {
// In this case, the value of 42 is existing in the range.
} else {
// In this case, the value of 42 is NOT existing in the range.
}
}
Note:
About var indexDataNumber = values.indexOf(42); for example always ends up being -1, I think that the reason of this issue is due to that values is 2 dimensional array. If you want to use this, you can also use the following script.
function myFunction() {
var sheet = SpreadsheetApp.getActiveSheet();
var rangeBikeNumbers = sheet.getRange("A5:A5000");
var values = rangeBikeNumbers.getValues();
var find = values.map(([e]) => e).indexOf(42); // of values.flat().indexOf(42);
if (find > -1) {
// In this case, the value of 42 is existing in the range.
} else {
// In this case, the value of 42 is NOT existing in the range.
}
}
References:
Benchmark: Process Costs for Searching Values in Spreadsheet using Google Apps Script
getDataRange()
getRange(a1Notation)
createTextFinder(findText)
Select any active range that you wish to search and it will search for the seed in that at range. The seed is currently defaulted to 42 but you can change it.
function findSeedInRange(seed = 42) {
const ui = SpreadsheetApp.getUi();
const ss = SpreadsheetApp.getActive();
const sh = ss.getActiveSheet();
const rg = sh.getActiveRange();
const row = rg.getRow();
const col = rg.getColumn();
var found = false;
rg.getValues().forEach((r, i) => {
r.forEach((c, j) => {
if (c == seed) {
let r = sh.getRange(i + row, j + col).getA1Notation();
ui.alert(`Found ${seed} in ${r}`);
found = true;
}
})
})
if(!found) {
ui.alert(`Did not find ${seed}`);
} else {
ui.alert('That is all.')
}
}
Here's another approach:
function findSeedInRange() {
const ui = SpreadsheetApp.getUi();
const ss = SpreadsheetApp.getActive();
const sh = ss.getActiveSheet();
const rg = sh.getActiveRange();
const resp = ui.prompt('Enter Seed', 'Enter Seed', ui.ButtonSet.OK_CANCEL)
if (resp.getSelectedButton() == ui.Button.OK) {
var seed = parseInt(resp.getResponseText());
const row = rg.getRow();
const col = rg.getColumn();
var found = false;
rg.getValues().forEach((r, i) => {
r.forEach((c, j) => {
if (c == seed) {
let r = sh.getRange(i + row, j + col).getA1Notation();
ui.alert(`Found ${seed} in ${r}`);
found = true;
}
});
});
if (!found) {
ui.alert(`Did not find ${seed}`);
} else {
ui.alert('That is all.')
}
} else {
ui.alert('Operation cancelled.')
}
}
I have a Google Sheet containing two sheets, one named Form and the other Data. Information on the Data sheet is usually entered via the Form sheet, where a button click on the Form sheet sends the data over to the bottom row on the Data sheet.
I'm trying to figure out how I can add a timestamp to the appropriate cell in column E on the Data sheet when a cell on the same row in column D receives the word 'CLEANED' either as a submission from the Form sheet or as a direct manual entry on the Data sheet.
This code currently adds the timestamp, but only when I change a value in column D directly on the Data sheet (not when values are added via the Form sheet), and it adds the timestamp for any value that's added.
var ColumnToCheck = 4;
var DateTimeLocation = [0,1];
var sheetName = 'Data'
function onEdit(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
if( sheet.getSheetName() == sheetName ) {
var selectedCell = ss.getActiveCell();
if( selectedCell.getColumn() == ColumnToCheck) {
var dateTimeCell = selectedCell.offset(DateTimeLocation[0],DateTimeLocation[1]);
dateTimeCell.setValue(new Date());
}
}
}
I am using this code to transfer data from the Form sheet:
function submitData() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var formSS = ss.getSheetByName("Form");
var dataSheet = ss.getSheetByName("Data");
var values = formSS.getRange("B2:B5").getValues().reduce(function(a, b) {
return a.concat(b)
});
var partNum = values[0];
var row;
dataSheet.getDataRange().getValues().forEach(function(r, i) {
if (r[0] === partNum) {
row = i + 1
}
})
row = row ? row : dataSheet.getLastRow() + 1;
var data = dataSheet.getRange(row, 1, 1, 4).getValues()[0].map(function (el, ind){
return el = values[ind] ? values[ind] : el;
})
dataSheet.getRange(row, 1, 1, 4).setValues([data]);
formSS.getRange("B2:B5").clearContent()
}
What would be the best way to have the date added automatically when only the word 'CLEANED' is entered? The date shouldn't clear if the word 'CLEANED' is removed or changed to a different word.
EDIT - SOLUTION
Thanks to Sourabh Choraria, I was able to change my UPDATE function to this in order to incorporate my needs (my additions to his code are commented):
function submitData() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var formSS = ss.getSheetByName("Form");
var dataSheet = ss.getSheetByName("Data");
var values = formSS.getRange("B3:B6").getValues().reduce(function(a, b) {
return a.concat(b)
});
var partNum = values[0];
var row;
dataSheet.getDataRange().getValues().forEach(function(r, i) {
if (r[0] === partNum) {
row = i + 1
}
})
row = row ? row : dataSheet.getLastRow() + 1;
var data = dataSheet.getRange(row, 1, 1, 4).getValues()[0].map(function (el, ind){
return el = values[ind] ? values[ind] : el;
})
var statusValue = formSS.getRange("B6").getValue(); //added
if (statusValue != 'CLEANED') { //added
dataSheet.getRange(row, 1, 1, 4).setValues([data]); //added
} //added
if (statusValue == 'CLEANED') { //added
var now = [new Date()];
var newData = data.concat(now)
dataSheet.getRange(row, 1, 1, 5).setValues([newData]);
} //added
formSS.getRange("B3:B6").clearContent()
}
Update the submitData() function with the following code -
function submitData() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var formSS = ss.getSheetByName("Form");
var dataSheet = ss.getSheetByName("Data");
var values = formSS.getRange("B2:B5").getValues().reduce(function(a, b) {
return a.concat(b)
});
var partNum = values[0];
var row;
dataSheet.getDataRange().getValues().forEach(function(r, i) {
if (r[0] === partNum) {
row = i + 1
}
})
row = row ? row : dataSheet.getLastRow() + 1;
var data = dataSheet.getRange(row, 1, 1, 4).getValues()[0].map(function (el, ind){
return el = values[ind] ? values[ind] : el;
})
// modification begins here
var now = [new Date()];
var newData = data.concat(now)
dataSheet.getRange(row, 1, 1, 5).setValues([newData]);
// modification ends here
formSS.getRange("B2:B5").clearContent()
}
Basically, I'm declaring a new array variable (now) and concatenating it to the existing data array, thus forming a "newData" variable and then updating the setValues function to accommodate this change.
Hope this helps and thanks for sharing the view access to the sheet - I made a copy of that and tested the solution directly :)
Edit note1
As for updating the sheet only when the dropdown selected is 'CLEANED', kindly modify your onEdit() function to accommodate the following IF condition -
var statusValue = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Form').getRange("B6").getValue();
if (statusValue == 'CLEANED') {
// update timestamp as required
}