I have the below working script but would like to have it working in multiple tabs, instead of having to create several triggers and overload the sheet.
This is the script:
function clearSomeCellsReading () {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName('Reading');
const sr = 1;
const vs = sh.getRange(sr,21,sh.getLastRow() - sr + 1).getValues().flat();
vs.forEach((e,i) => {
if(e > 29) sh.getRange(i + sr,16,1,3).clearContent();
})
}
Apart from "('Reading')", I have other 20 locations that I would like to incorporate, ie. Bristol, Watford...
All the other tabs are exactly the same to this one, all copies of each other with just different data.
I tried duplicating the script and just changing the tab name but I keep getting errors...
Any help would be greatly appreciated.
Thank you!
I think this might be what you're looking for. If you have some distinct text to identify the Sheets you want to run the procedure on, that would make it dynamic. Otherwise you can specify the exact names of the sheets you want to run it on as shown below.
function allSheetsMacro(){
const ss = SpreadsheetApp.getActiveSpreadsheet();
const theSheetsToInclude = ["Reading","Bristol","Watford"];
const allSheets = ss.getSheets();
for(var i=0;i<allSheets.length;i++){
var aSheet = allSheets[i];
if(theSheetsToInclude.includes(aSheet.getName())){
//or whatever rule to find your sheets.
clearSomeCellsReading_(aSheet);
}
}
}
function clearSomeCellsReading_(sh) {
//original function but being called each time from first function.
const sr = 1;
const vs = sh.getRange(sr,21,sh.getLastRow() - sr + 1).getValues().flat();
vs.forEach((e,i) => {
if(e > 29) sh.getRange(i + sr,16,1,3).clearContent();
})
}
function clearSomeCellsReading () {
const ss = SpreadsheetApp.getActive();
const sh = ss.getActiveSheet();
const sr = 1;
const vs = sh.getRange(sr,21,sh.getLastRow() - sr + 1).getValues().flat();
vs.forEach((e,i) => {
if(e > 29) sh.getRange(i + sr,16,1,3).clearContent();
})
}
or maybe this:
function clearSomeCellsReading() {
const shts = ['Reading', 'others'];
const ss = SpreadsheetApp.getActive();
const sh = ss.getActiveSheet();
if (~shts.indexOf(sh.getName())) {
const sr = 1;
const vs = sh.getRange(sr, 21, sh.getLastRow() - sr + 1).getValues().flat();
vs.forEach((e, i) => {
if (e > 29) sh.getRange(i + sr, 16, 1, 3).clearContent();
})
}
}
Related
I'm building a calculator to use for pricing purposes. It has a primary "Calculator" sheet, where an admin can enter data and then generate a new result to the "DataLog" sheet. The "DataLog" sheet stores the results (columns A through X) and calculates the resulting price (columns Y through AO). There are also a few workflow columns that need to be present for each row (Columns AP through AS).
I am currently using appendRow() to print the data to the "DataLog" sheet. The issue is that appendRow() finds the first empty row, and since columns Y through AS are not empty because they contain necessary formulas/workflow, it prints to the bottom of the sheet. I am looking for a way to print the data where 1) it checks only a certain column for an empty row (column A or C, for example) and prints to that row, and 2) does not overwrite the formula/workflow columns (Y through AS).
Is there a way to do this using appendRow() or is there another function I should be using? Other than this one issue of where to print the results, everything works just as I want it to, but I cannot seem to find a way to resolve this issue.
EDIT: The reason the formula and workflow must be present within "DataLog" is that there are situations where after an entry has been filled out and printed changes need to be made to row, thereby changing the final price. So I cannot calculate the price within the function and print that as a static number.
Here is a copy of the calculator: https://docs.google.com/spreadsheets/d/1vsVZeOUUqhdiW1unz6dPuiP5yw24ENrv1-49kXqBnx4/edit#gid=0
Here is a copy of the code I am using:
function ClearCells() {
var sheet = SpreadsheetApp.getActive().getSheetByName('CALCULATOR');
sheet.getRange('G9:H9').clearContent();
sheet.getRange('G11').clearContent();
sheet.getRange('G14:H14').clearContent();
sheet.getRange('G6').clearContent();
sheet.getRange('I6').clearContent();
sheet.getRange('I17:I21').clearContent();
sheet.getRange('I24:I29').clearContent();
sheet.getRange('I32').clearContent();
sheet.getRange('K5').clearContent();
sheet.getRange('K15').clearContent();
}
function FinalizePrice() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sourceRangeFL = ss.getRangeByName('FirstLast');
const sourceValsFL = sourceRangeFL.getValues().flat();
const sourceRangeEN = ss.getRangeByName('EntityName');
const sourceValsEN = sourceRangeEN.getValues().flat();
const sourceRangeEP = ss.getRangeByName('EmailPhone');
const sourceValsEP = sourceRangeEP.getValues().flat();
const sourceRangeRT = ss.getRangeByName('ReturnType');
const sourceValsRT = sourceRangeRT.getValues().flat();
const sourceRangeRE = ss.getRangeByName('Returning');
const sourceValsRE = sourceRangeRE.getValues().flat();
const sourceRangeBQ = ss.getRangeByName('BasicQuestions');
const sourceValsBQ = sourceRangeBQ.getValues().flat();
const sourceRangeSEQ = ss.getRangeByName('SchEQuestions');
const sourceValsSEQ = sourceRangeSEQ.getValues().flat();
const sourceRangeEQ = ss.getRangeByName('EntityQuestions');
const sourceValsEQ = sourceRangeEQ.getValues().flat();
const sourceRangePYP = ss.getRangeByName('PYP');
const sourceValsPYP = sourceRangePYP.getValues().flat();
const sourceRangeADJ = ss.getRangeByName('Adjustment')
const sourceValsADJ = sourceRangeADJ.getValues().flat();
const sourceRangeAN = ss.getRangeByName('AdjustmentNote')
const sourceValsAN = sourceRangeAN.getValues().flat();
const sourceVals = [...sourceValsFL, ...sourceValsEN, ...sourceValsEP, ...sourceValsRT, ...sourceValsRE, ...sourceValsBQ, ...sourceValsSEQ, ...sourceValsEQ, ...sourceValsPYP, ...sourceValsADJ, ...sourceValsAN]
console.log(sourceVals)
const anyEmptyCell = sourceVals.findIndex(cell => cell === "");
if(anyEmptyCell !== -1){
const ui = SpreadsheetApp.getUi();
ui.alert(
"Input Incomplete",
"Please enter a value in ALL input cells before submitting",
ui.ButtonSet.OK
);
return;
}
const date = new Date();
const email = Session.getActiveUser().getEmail();
const data = [date, email, ...sourceVals];
const destinationSheet = ss.getSheetByName("DataLog");
destinationSheet.appendRow(data);
console.log(data);
sourceRangeFL.clearContent();
sourceRangeEN.clearContent();
sourceRangeEP.clearContent();
sourceRangeRT.clearContent();
sourceRangeRE.clearContent();
sourceRangeBQ.clearContent();
sourceRangeSEQ.clearContent();
sourceRangeEQ.clearContent();
sourceRangePYP.clearContent();
sourceRangeADJ.clearContent();
sourceRangeAN.clearContent();
ss.toast("Success: Item added to the Data Log!");
}
I know this is incomplete but for the purpose of discussion here's how I would clear content in your situation.
function ClearCells() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName('Sheet0');
sh.getRangeList(["G9", "G11", "G14:H14", "G6", "I6", "I17:I21", "I24:I29", "I32", "K5", "K15"]).getRanges().forEach(r => r.clearContent();)
}
If you wished to append the values of your individual ranges into a row you could do it like this:
function appendRangeValues() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName('Sheet0');
const osh = ss.getSheetByName('Sheet1')
const rgl = sh.getRangeList(["G9", "G11", "G14:H14", "G6", "I6", "I17:I21", "I24:I29", "I32", "K5", "K15"]);
const rglb = breakUpRangeList(ss,sh,rgl);
const vs = rglb.getRanges().map(r => r.getValue());
Logger.log(JSON.stringify(vs))
osh.getRange(osh.getLastRow() + 1, 1, 1, vs.length).setValues([vs]);
}
But I'm guessing that you want to skip over cell functions and other columns so let me know what you want and may be we can find a solution that fits your needs
The breakUpRangeList function is something I wrote a while back to break up ranges into their individual cells which I find easier to deal with.
function breakUpRangeList(ss=SpreadsheetApp.getActive(),sh=ss.getSheetByName("Sheet0"),rgl) {
let b = [];
rgl.getRanges().forEach(rg => {
rg.getValues().forEach((r,i) => {
let row = rg.getRow() + i;
r.forEach((c, j) => {
let col = rg.getColumn() + j;
b.push(sh.getRange(row, col).getA1Notation())
})
})
})
b = [...new Set(b)];
Logger.log(JSON.stringify(b));
return sh.getRangeList(b);
}
Try this:
function appendRangeValues() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName('Sheet0');
const osh = ss.getSheetByName('Sheet1')
const rgl = sh.getRangeList(["G9", "G11", "G14:H14", "G6", "I6", "I17:I21", "I24:I29", "I32", "K5", "K15"]);
const rglb = breakUpRangeList(ss,sh,rgl);
const vs = rglb.getRanges().map(r => r.getValue());
Logger.log(JSON.stringify(vs))
osh.getRange(getColumnHeight(3,osh,ss) + 1, 1, 1, vs.length).setValues([vs]);
}
function getColumnHeight(col, sh, ss) {
var ss = ss || SpreadsheetApp.getActive();
var sh = sh || ss.getActiveSheet();
var col = col || sh.getActiveCell().getColumn();
var rcA = [];
if (sh.getLastRow()){ rcA = sh.getRange(1, col, sh.getLastRow(), 1).getValues().flat().reverse(); }
let s = 0;
for (let i = 0; i < rcA.length; i++) {
if (rcA[i].toString().length == 0) {
s++;
} else {
break;
}
}
return rcA.length - s;
}
I am currently using this script to move (selected) rows from one google sheet into another using checkbox.
It moves the rows fine but the move keeps duplicating if I do not physically go back and uncheck each row.
1)Am I using the correct script to copy the selected data?
2)and if so what can be done to prevent duplicates?
3)How do i make the checkbox to be a part of the code as well?
Thanks for whatever help I can get.
function moveChecked(){
const sh = SpreadsheetApp.getActive();
const ss = sh.getSheetByName("Original Data Sheet");
const outSheet = sh.getSheetByName("Sheet1");
let data = ss.getRange(2,1,ss.getLastRow()-1,48).getValues();
let out = [];
for (let i = 0; i< data.length; i++){
if (data[i][0] == true){
out.push(data[i]);
}
}
outSheet.getRange(outSheet.getLastRow()+1,1,out.length,48).setValues(out);
}
Move Checked
function moveChecked() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName("Sheet0");
const vs = sh.getRange(2, 1, sh.getLastRow() - 1, 48).getValues();
const osh = ss.getSheetByName("Sheet1");
let out = [];
//let d = 0;
vs.forEach((r, i) => {
if (r[0] == true) {
out.push(r);
//sh.deleteRow(i + 2 - d++)
}
});
osh.getRange(osh.getLastRow() + 1, 1, out.length, out[0].length).setValues(out)
}
I want to group people by the state they are living in. How can I do that using googles app script? I thought of using a forEach loop to group them but im not too sure which direction to go from there.
enter image description here
function sortByState() {
const sA = ["AL","AK","AS","AZ","AR","CA","CO","CT","DE","DC","FL","GA","GU","HI","ID","IL","IN","IA","KS","KY","LA","ME","MD","MA","MI","MN","MS","MO","MT","NE","NV","NH","NJ","NM","NY","NC","ND","MP","OH","OK","OR","PA","PR","RI","SC","SD","TN","TX","UT","VT","VA","VI","WA","WV","WI","WY"];
const ss = SpreadsheetApp.getActive();
const sh = ss.getActiveSheet();
const hA = sh.getRange(1, 1, 1, sh.getLastColumn()).getValues()[0];
let idx = {};
hA.forEach((h, i) => { idx[h] = i; });
const rg = sh.getRange(2, 1, sh.getLastRow() - 1, sh.getLastColumn());
const vs = rg.getValues().sort((a,b) => {
vA = sA.indexOf(a[idx['State']]);
vB = sA.indexOf(b[idx['State']]);
return vA - vB;
});
rg.clearContent();
sh.getRange(2,1,vs.length,vs[0].length).setValues(vs);
}
I have a sheet with more than 3000 rows and I want to group those rows by a parameter in ColA. So all rows having '1' in colA should be grouped under the row above with '0' in colA. Once groups are created I want them to collapse.
Since the script will be triggered daily upon data update, I also need the before-created groups to be removed so the new ones can be properly created.
I have several scrips doing what I need but it takes forever for them to go through all the rows. Is it possible to optimize them in some way or perhaps a different approach can be used for my needs? Thanks in advance for your help!
function removeAllGroups1() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName("Pipeline");
const rg = sh.getDataRange();
const vs = rg.getValues();
vs.forEach((r, i) => {
let d = sh.getRowGroupDepth(i + 1);
if (d >= 1) {
sh.getRowGroup(i + 1, d).remove()
}
});
}
function groupRows1() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName('Pipeline');
const levels = sh.getRange(2, 1, sh.getLastRow() - 1).getValues().flat();
levels.forEach((e, i) => sh.getRange(i + 2, 1).shiftRowGroupDepth(e));
}
function collapse() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName('Pipeline');
let lastRow = sh.getDataRange().getLastRow();
for (let row = 1; row < lastRow; row++) {
let depth = sh.getRowGroupDepth(row);
if (depth < 1) continue;
sh.getRowGroup(row, depth).collapse();
}
}
Data Sample: https://docs.google.com/spreadsheets/d/10BNrnAyQw89gy-Sj3CLiz4AgVtFI0AjXTvc0REGGTfY/edit#gid=113574154
I believe your goal is as follows.
You want to group the rows with high process speed.
You want to delete all groups with high process speed.
You want to collapse all groups with high process speed.
From the above goal, I thought that when Sheets API is used, the process cost can be reduced. And, Sheets API can group rows, delete all groups and collapse all groups.
Sample script:
Before you run this script, please enable Sheets API at Advanced Google services.
function removeAllGroups2() {
const sheetName = "sample";
const ss = SpreadsheetApp.getActive();
const ssId = ss.getId();
const sheetId = ss.getSheetByName(sheetName).getSheetId();
const n = Sheets.Spreadsheets.get(ssId, { ranges: [sheetName] }).sheets[0].rowGroups.reduce((n, { depth }) => n < depth ? depth : n, 0);
const requests = Array(n).fill("").map(_ => ({ deleteDimensionGroup: { range: { sheetId, dimension: "ROWS" } } }));
Sheets.Spreadsheets.batchUpdate({ requests }, ssId);
}
function groupRows2() {
const sheetName = "sample";
const ss = SpreadsheetApp.getActive();
const sheet = ss.getSheetByName(sheetName);
const levels = sheet.getRange("A2:A" + sheet.getLastRow()).getValues();
const sheetId = sheet.getSheetId();
const requests = levels.flatMap(([a], i) => Array(a).fill("").map(_ => ({ addDimensionGroup: { range: { sheetId, startIndex: i + 1, endIndex: i + 2, dimension: "ROWS" } } })));
Sheets.Spreadsheets.batchUpdate({ requests }, ss.getId());
}
function collapse2() {
const ss = SpreadsheetApp.getActive();
const requests = Sheets.Spreadsheets.get(spreadsheetId, {ranges: ["sample"]}).sheets[0].rowGroups.map(r => {
r.collapsed = true;
return { updateDimensionGroup: { fields: "*", dimensionGroup: r }};
});
Sheets.Spreadsheets.batchUpdate({ requests }, ss.getId());
}
Note:
I tested these sample scripts using your sample Spreadsheet. So when the structure of the Spreadsheet is different from your sample Spreadsheet, these scripts might not be able to be used. Please be careful about this. At first, please test them using your sample script.
References:
Method: spreadsheets.get
Method: spreadsheets.batchUpdate
DeleteDimensionGroupRequest
AddDimensionGroupRequest
UpdateDimensionGroupRequest
This function works fine. When the current sheet is not a Google form response sheet. I am using (Everyminute) Trigger
Image of: Sand data (mainData) sheet to (shareData1) sheet
I am looking for a, When the user submits the Google form whose status is ( Paid ), the data should go on the second sheet which is (shareData1)
The problem here is that this function will not work anymore when I am using the Google Forms response sheet. And it shows an error.
Error Image
in the error image for demotion, I am run this function manually, the same error show in Executions AppScript panel.
function doneCopy() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("mainData");
var values = sheet.getRange(1, 3, sheet.getLastRow(), 1).getValues();
var moveRows = values.reduce(function(ar, e, i) {
if (e[0] == "Paid" ) ar.push(i + 1)
return ar;
}, []);
var targetSheet = ss.getSheetByName("shareData1");
moveRows.forEach(function(e) {
sheet.getRange(e, 1, 1, sheet.getLastColumn()).moveTo(targetSheet.getRange(targetSheet.getLastRow() + 1, 1));
});
moveRows.reverse().forEach(function(e) {sheet.deleteRow(e)});
}
function doneCopy() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sh = ss.getSheetByName("mainData");
const tsh = ss.getSheetByName("shareData1");
const vs = sh.getRange(1, 3, sh.getLastRow(), 1).getValues().flat();
const n = sh.getLastColumn();
let d = 0;
vs.forEach((e, i) => {
if (e == 'Paid') {
sh.getRange(i + 1, 1, 1, n).copyTo(tsh.getRange(tsh.getLastRow() + 1, 1));
sh.deleteRow(i + 1 - d++);
}
});
}
I'm not sure what your additional needs are but I'll take my best guess.
function doneCopy() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sh = ss.getSheetByName("mainData");
const tsh = ss.getSheetByName("shareData1");
const vs = sh.getRange(1, 3, sh.getLastRow(), 1).getValues().flat();
const n = sh.getLastColumn();
const a = ['Paid','Process'];
let d = 0;
vs.forEach((e, i) => {
if (~a.indexOf(e)) {
sh.getRange(i + 1, 1, 1, n).copyTo(tsh.getRange(tsh.getLastRow() + 1, 1));
sh.deleteRow(i + 1 - d++);
}
});
}