Copy last updated row from Sheet1 to Sheet2 - google-apps-script

data.forEach(function(f) {
if (~f.indexOf(searchtext)) {
ar.push(f);
}
});
return ar;
}
function isUpdated(data) {
var arrayLength = data.length;
var updated = false;
var oldData = search(data[1]);
for (var i = 1; i < arrayLength; i++) {
if(oldData[0][i] != data[i]){
updated = true;
break;
}
}
return updated;
}
function update(data){
data = JSON.parse(data)
const tag = data[1].toString().trim().toUpperCase()
const ws = SpreadsheetApp.openById(SS_ID).getSheetByName(SN_DATA)
const values = ws.getDataRange().getValues()
const index = values.findIndex(v => v[1].toString().trim().toUpperCase() === tag)
if (index === -1) return false
ws.getRange(index + 1, 1, 1, data.length).setValues([data])
return true
}
Hello expert I'm able to write a code to search and update using Web App. google Sheet Sheet1 can anyone help ,e how to copy my last edited/updated rows to sheet2? I want to add row function every time the sheet1 has been updated the updated column will logs to sheet2? code has been provide above

Related

Checking two columns and grouping to validate entries

I have data sheet as below-
I am trying to check for values in column Count of deletions if it is empty or not a number to flag an error at an account level,
because there will be only one entry of Count of deletions which will be at the first row entry of Account ID (unique) as illustrated in the pic above.
I did this to just validate the column Count of deletions
function myFunction() {
SS = SpreadsheetApp.getActiveSpreadsheet();
var sheet = SS.getSheetByName("Sheet1");
var Validation_sheet = SS.getSheetByName("Sheet2");
var last_row = sheet.getLastRow();
var values = sheet.getRange(2,5,last_row).getValues();
var Status_values = sheet.getRange(2,4,last_row).getValues();
var result = isnumberorEmpty(values, Status_values);
const Count_setvalue = Validation_sheet.getRange("A2");
var errors_c = [];
for (key in result){
var check1 = result[key] == "empty";
var check2 = result[key] == "Not a number";
if(check1 == true || check2 == true){
errors_c.push(key);
}
}
if (errors_c.length > 0){
Count_setvalue.setValue("Row(s) " + errors_c.join(" and ") + " have error");
}
else{
Count_setvalue.setValue("No errors");
}
}
function isnumberorEmpty(array, array1) {
let result = {};
for (let i = 0; i < array.length-1; i++) {
if( array1[i] == "Completed"){
let row = array[i];
if (row[0] === "") {
result[i+2] = "empty";
}
else if (row[0] === "Ignored") {
result[i+2] = "No error";
}
else if (!isNaN(row[0])) {
result[i+2] = "number";
}
else {
result[i+2] = "Not a number";
}
}
else{
}
}
return result;
}
The above checks for column Count of deletions and validates only where corresponding rows in column Status has Completed.
The output will show me error for rows, 8 and 10-18,
but it should not as the values are entered at the account level.
However, there should be error for only Account4 and Account5, meaning the error should be only for Row 16 and row 18 as below -
From your updated question, I thought that the reason for your current issue might be due to that you checked columns "D" and "E". In order to achieve However, there should be error for only Account4 and Account5, meaning the error should be only for Row 16 and row 18 as below -, how about checking the columns "A", "D" and "E"? In this case, how about the following sample script?
Sample script 1:
function myFunction1() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
// Process in the source sheet.
const srcSheet = ss.getSheetByName("Sheet1");
const values = srcSheet.getRange("A2:E" + srcSheet.getLastRow()).getDisplayValues();
const { errors_c } = values.reduce((o, r, i) => {
if (i == 0 || o.temp != r[0]) {
o.temp = r[0];
if (r[3] == "Completed" && !r[4]) {
o.errors_c.push(i + 2);
}
}
return o;
}, { errors_c: [], temp: "" });
// Process in the destination sheet.
const dstRange = ss.getSheetByName("Sheet2").getRange("A2");
if (errors_c.length > 0) {
dstRange.setValue("Row(s) " + errors_c.join(" and ") + " have error");
} else {
dstRange.setValue("No errors");
}
}
In this modification, the columns "A", "D" and "E" are checked.
When this script is tested to your sample input Spreadsheet, Row(s) 16 and 18 have error is put to the cell "A2" of "Sheet2".
Sample script 2:
Although unfortunately, I'm not sure whether I could correctly understand your expected result, if you want to check all values of column "D" for each "Account ID", how about the following sample script?
function myFunctionc2() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
// Process in the source sheet.
const srcSheet = ss.getSheetByName("Sheet1");
const values = srcSheet.getRange("A2:E" + srcSheet.getLastRow()).getDisplayValues();
const obj = values.reduce((m, r, i) => m.set(r[0], m.has(r[0]) ? { v: [...m.get(r[0]).v, r[3]], i: m.get(r[0]).i } : { v: [r[3]], i: i + 2 }), new Map()).values();
const errors_c = [...obj].reduce((ar, { v, i }) => {
if (v.every(e => e == "Completed") && !values[i - 2][4]) {
ar.push(i);
}
return ar;
}, []);
// Process in the destination sheet.
const dstRange = ss.getSheetByName("Sheet2").getRange("A2");
if (errors_c.length > 0) {
dstRange.setValue("Row(s) " + errors_c.join(" and ") + " have error");
} else {
dstRange.setValue("No errors");
}
}
Reference:
reduce()

split the sheet into different workbooks by column values with dynamic range in apps script

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:

My google app script takes longer to run how can I minimize the run time?

function test() {
var ss1 = SpreadsheetApp.getActiveSpreadsheet();
var ss = ss1.getActiveSheet();
var only = ['AutoGR', 'Sheet2', 'Sheet3', 'Sheet4'];
if (only.indexOf(ss.getName()) == -1) return;
var numRows = SpreadsheetApp.getActiveSheet().getLastRow();
var range;
for (var i = 1; i <= numRows; i++) {
range = ss.getRange('B' + i);
if (range.getValues() == "Crate") {
range.offset(0, 4).setValue(3000);
}
}
for (var i = 1; i <= numRows; i++) {
range = ss.getRange('B' + i);
if (range.getValues() == "Pallet") {
range.offset(0, 4).setValue(4200);
}
}
}
What I am trying to achieve is if values in column B = ‘Crate’ then in the same row under column named Qty put value automatically as 3000, if values in column B = ‘Pallet’ then Qty column automatically gets updated as 4200 but when B=‘close operation’ I have to add qty manually.
Setting Column F in multiple sheets using getValues() and setValues()
function test() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const incl = ['AutoGR', 'Sheet2', 'Sheet3', 'Sheet4'];
const shts = ss.getSheets().filter(sh => ~incl.indexOf(sh.getName()));
shts.forEach(sh => {
let bs = sh.getRange(1,2,sh.getLastRow()).getValues();
let fs = sh.getRange(1,6,sh.getLastRow()).getValues();
bs.forEach((r,i) => {
if(r[0] == "Crate") {
fs[i][0] = 3000;
} else if(r[0] == "Pallet") {
fs[i][0] = 4200;
}
})
sh.getRange(1,6,fs.length,1).setValues(fs);
})
}
If this causes problems in other cells in F then you may be forced to write to F one cell at a time but you'll still get better performance on the read side.

Pivot or Transpose data in a google sheet using apps script

I'm hoping someone can assist me with either pivoting or transposing data within a google sheet programmatically using apps script.
Below is what I've done so far. I'm pretty sure this is far from the correct/optimum way of achieving this hence me reaching out here. The data is for a survey with 3 questions. The script runs fine but doesn't account for any surveys where there where no answers selected for a certain question or for any one of the 3 questions. I'm pretty sure that's because there needs to be an else statement part of each If statement but can't figure out how to write that logic within each loop.
I also tried experimenting by converting this to and array of objects, where I tried using the ID key to to match the date, source, and three questions but couldn't get that working either.
I've attached images of what the source data looks like and what I'm trying to achieve, as well as what it currently looks like after I execute the script I currently have.
The code I've written is after the 3 images I've uploaded.
Hope I've explained this all correctly. I'd appreciate any assistance with this.
The source data looks like this:
And this is what I am trying to achieve:
This is what the data looks like once the above code has run:
function sample() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('sampleData');
var range = sheet.getRange(2,1,sheet.getLastRow()-1,sheet.getLastColumn());
var values = range.getValues();
var resultsSh = ss.getSheetByName('sampleResults');
//console.log(values);
var source = values.filter(function(row){
if(row[2] === 'Source'){
return true;
} else {
return false;
}
});
//console.log(source);
var q1 = values.filter(function(row){
if(row[2] === 'Question1'){
return true;
} else {
return false;
}
});
//console.log(q1);
var q2 = values.filter(function(row){
if(row[2] === 'Question2'){
return true;
} else {
return false;
}
});
//console.log(q2);
var q3 = values.filter(function(row){
if(row[2] === 'Question3'){
return true;
} else {
return false;
}
});
//console.log(q3);
var result1 = [];
for (i=0;i<source.length;i++){
for (j=0;j<q1.length;j++){
if(source[i][1] === q1[j][1]){
result1.push([...source[i], ...q1[j]]);
}
}
}
//console.log(result1);
var result2 = [];
for (i=0;i<result1.length;i++){
for (j=0;j<q2.length;j++){
if (result1[i][1] === q2[j][1]) {
result2.push([...result1[i],...q2[j]])
}
}
}
//console.log(result2);
var final = [];
for (i=0;i<result2.length;i++) {
for (j=0;j<q3.length;j++) {
if (result2[i][1] === q3[j][1]) {
final.push([...result2[i], ...q3[j]])
}
}
}
//console.log(final);
var data = final.map(function(row){
return [row[0].toLocaleString('en-GB').replace(/[',']/g,''), row[1], row[3], row[7], row[11], row[15], row[7] + row[11] + row[15]];
});
console.log(data);
ss.getSheetByName('Sheet16').getRange(2, 1, data.length, data[0].length).setValues(data);
}
This can be done with a single for loop that checks each row and a switch() case: to fill up the respective array elements and compute for the sum.
function sample() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('sampleData');
var range = sheet.getRange(2,1,sheet.getLastRow()-1,sheet.getLastColumn());
var values = range.getValues();
var resultsSh = ss.getSheetByName('sampleResults');
var lastRow = sheet.getLastRow();
var lastID = values[0][1];
var result = [];
var sum = 0;
// check each row
for (i=0; i<lastRow-1; i++) {
var currID = values[i][1];
if ((currID != lastID) & sum) {
result[result.length-1] = sum;
}
switch (values[i][2]) {
case 'Source':
result.push(values[i][0],values[i][1],values[i][3],'','','','');
sum = 0;
break;
case 'Question1':
result[result.length-4] = values[i][3];
sum += values[i][3];
break;
case 'Question2':
result[result.length-3] = values[i][3];
sum += values[i][3];
break;
case 'Question3':
result[result.length-2] = values[i][3];
sum += values[i][3];
break;
}
lastID = currID;
}
result[result.length-1] = sum;
// convert to 2d array
const result2d = [];
while(result.length) result2d.push(result.splice(0,7));
// put to results sheet
var resultRange = resultsSh.getRange(2,1,result2d.length,result2d[0].length);
resultRange.setValues(result2d);
}
Sample Data:
Sample Results:

Google sheets Script to Hide Rows in Batch

I'm currently using this script to hide rows containing 0 on col K
function Hide() {
var s = SpreadsheetApp.getActive()
.getSheetByName('Sheet1');
s.getRange('K:K')
.getValues()
.forEach(function (r, i) {
if (r[0] !== '' && r[0].toString()
.charAt(0) == 0) s.hideRows(i + 1)
});
}
Which works perfect, the only thing is that here when I run the script, it hides row by row (now that I have a lot of rows it takes so much time).
Is there a way to change it to work in batch?
This is the script that makes the magic
function Hide() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Ventas");
var currentRange = ss.getRangeByName("RangeCalculation");
var rangeStart = currentRange.getRow();
var values = currentRange.getValues();
var index = 0, rows = 1;
var show = !(values[0][12] == "" );
for (var i = 1, length = values.length; i < length; i++) {
if (values[i][0] == 1 ) {
if (show) {
sheet.showRows(rangeStart + index, rows);
show = false;
index = i;
rows = 1;
} else
rows++;
} else {
if (show)
rows++;
else {
sheet.hideRows(rangeStart + index, rows);
show = true;
index = i;
rows = 1;
}
}
}
if (show)
sheet.showRows(rangeStart + index, rows);
else
sheet.hideRows(rangeStart + index, rows);
}
Instead of hideRows(rowIndex), use hideRows(rowIndex, numRows)
The first form use only one parameter rowIndex, the second use two parameters, rowIndex and numbRows.
Obviously, using the suggested method implies to review the logic of your script.