So I am taking data from a spreadsheet, putting the relevant parts into an array and writing it back to a new spreadsheet. The data is coming from a google form and not everyone had an input for every question so I am taking only the questions that are not null. Which causes the length of my array to be variable
So I have taken it down to its most barest steps and I am stuck at the part where you insert the sheet, it works fine on a iteration of 1, but when I do it on a loop there is an error.
for (j=2; j <= 3 ; j++)// I have 23 iterations, but I stopped at 2 for now
{
var cellLocation = new String ("B" + j + ":AC" + j);
Logger.log("J is " + j + "Cell location " + cellLocation);
var workingRange = buySheet.getRange(cellLocation);
var customer = workingRange.getValues(); //this produces a range of the row
Logger.log("Range is " + workingRange.getA1Notation() + " data is " + customer[0][0]);
//At first I thought it was the range- so I broke everything out into the tiniest step
var nameCustomer = new String(customer[0][0]);
Logger.log("nameCustomer is " + nameCustomer);
//this correctly prints out the names as strings- through 23 iterations
var create = buySheet.insertSheet(nameCustomer);
//this is a sheet object with the customers name- fails after the first iteration, and creates sheets with blank names -
}
This is the reference I am using: https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet#insertSheet(String)
I have commented out everything after this to solve this one sticking point. I have tried instantiating the string outside the loop. I can post the logs, I am using real data I have so I would have to blur out peoples names.
It works for the first round, and it works if I manually type a string.
I figured it out! When I created a new sheet it moved the active sheet to the new sheet. So at the end of the loop I have to reset the active sheet to the first sheet where the data I want it is.
Seems so obvious in retrospect.
Related
When I duplicated a sheet with already defined Named Ranges (say NamedRange), the new sheet's (NewSheet) Named Ranges automatically created the new Named Ranges as: NewSheet!NamedRange_conflict425783334.
Renaming the new sheet (From NewSheet to RenamedNamedRange) automatically renames the Named Ranges as: RenamedNewSheet!NamedRange_conflict425783334.
NewSheet (Please click this link to make a copy of the NewSheet)
NewSheet (Please click this link to edit the NewSheet)
Question 1: Why duplicating sheet added Conflict489242791 to the Named Ranges?
My current Named Ranges looks something like this: (In my case, NewSheet is Mumbai)
I defined two variable as follows:
var ss = SpreadsheetApp.getActive();
var namedRanges = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Mumbai").getNamedRanges();
The Named Range array content can be logged by running displayNamedRange() :
function displayNamedRange() {
for (i = 0; i < namedRanges.length; i++) {
Logger.log(i + " = " + namedRanges[i].getName());
}
}
On running the function createNamedrange():
function createNamedRange() {
for (i = 0; i < namedRanges.length; i++) {
Logger.log(i + " = " + namedRanges[i].getName());
var tempName = RegExp("('Mumbai'!)([a-z_.]+)_(conflict)[0-9]+", "gi").exec(namedRanges[i].getName())[2];
//'Mumbai'!PRIMARY_KEY_conflict2080330731 Named Range will give PRIMARY_KEY
var range = ss.getSheetByName("Mumbai").getRange(namedRanges[i].getRange().getA1Notation());
namedRanges[i].remove();
ss.setNamedRange(tempName + "_Mumbai", range); //This created Named Range but with Number Boundaries C1:C1000 and not C:C
Logger.log(tempName + "_Mumbai");
Logger.log(i + " = " + namedRanges[i].getName());
}
}
I get:
and the Named Ranges changes as follows:
Question 2: Notice in the Execution Log, logging the new Named Ranges as soon it was created showed null, Why?
But when I run the displayNamedRange() again it logs the New Named Ranges perfectly as follows:
1st RUN:
Every time after I run createNamedRange(), displayNamedRange() gives different output like on running it for the second time displayNamedRange() gives:
2nd RUN:
If you see the 0th index of 1st RUN, we got T_FROM_Mumbai & on 2nd RUN we got BALANCE_METER_Mumbai and both of which is not same as the original Named Range Array's 0th index which was 'Mumbai'!DIO_NO._conflict1572891386.
To understand this behavior, I just simply only removed the 0th index of the original Named Range array, which is Mumbai'!DIO_NO._conflict1572891386 and ran the displayNamedRange(), I noticed all the indexes automatically randomly rearranged its positions.
Question 3: Why removing the Named Range from 0th index (or Nth index) doesn't empty that index & why does the array automatically randomly rearranges its indexes, what exactly is happening when the Named Ranges are removed or added.
Question 4: By running createNamedrange() function, the newly added Named Ranges range have Number Boundaries like C1:C1000, How do I achieve open range like C:C.
Hi and thank you in advance for your help--I may need a lot of it!
When I manually run my code it does pull the targeted formulas down and populates the cells I intend so that's nice but then nothing else beyond that in the code gets executed. If afterwards I run my code a second time, the remaining code does what I want it to do--message the user about bad data entry.
Can someone help me understand why my code does not continue on and execute all the lines?
I can understand my script is heavy, so I've tried placing utilities.sleep(3000) delays before a get command (I have a few in my code) but that did not work. I have also tried to parse out this code into separate functions but then I got stuck calling two spreadsheet-sourced triggers one after the other and that did not work well either.
Also, triggers are not working with this code--the error message I get is "Exception: Cannot call SpreadsheetApp.getUi() from this context." This is odd because when I manually execute, this line and the ui.alert() works just fine. Any ideas what is happening here?
My code is lacking organization and efficiencies, I'm sure--sorry for the challenge when reviewing these lines.
here's my code
function fillCheckNotifyDataEntry(){
//Check to see if function ran via trigger
Logger.log('function was triggered and did run');
var spreadsheet = SpreadsheetApp.openById("1cUdqsI0J6HoCAAbxeC3vSZBvNCSNyZ4CG0D23iBNfkM");
var sheet = SpreadsheetApp.setActiveSheet(spreadsheet.getSheets()[0]);
//Scan the Data Entry Okay column to check for the most recently added cell value and store that row number as lastRow
var lastRow = sheet.getLastRow();
var rangeDataEntryOkay = sheet.getRange('L:L' +lastRow);
//Get the value for Data Entry Okay column's most recently added cell to later check if the data entered was good or bad
var checkDataEntryRange = sheet.getRange('L' +lastRow);
var checkDataEntry = checkDataEntryRange.getValue();
//Get the details from the most recent data entry to the sheet
var rowOfInterestTime = sheet.getRange('B'+lastRow).getValue();
var rowOfInterestEmail = sheet.getRange('I'+lastRow).getValue();
var rowOfInterestTestedWhat = sheet.getRange('C'+lastRow).getValue();
var rowOfInterestFailedInspection = sheet.getRange('E'+lastRow).getValue();
var rowOfInterestFailedTest = sheet.getRange('F'+lastRow).getValue();
var rowOfInterestTotalAccepted = sheet.getRange('G'+lastRow).getValue();
var rowOfInterestTotalTested = sheet.getRange('H'+lastRow).getValue();
//Calculate the expected total number tested by adding all the failures with the number accepted
var rowOfInterestTotalExpected = (rowOfInterestFailedInspection + rowOfInterestFailedTest + rowOfInterestTotalAccepted);
//Triggers a pop-up window to appear with a message to the user
var ui = SpreadsheetApp.getUi();
//Pull the formulas in the columns Total Defects, Yield, and Data Entry Okay? down one row to populate that next row based on the lastRow value
spreadsheet.getRange('J3:L3').activate();
//Scan each successive row in column L starting with L3 to find the last one that contains a value and return that row number
if (rangeDataEntryOkay.getValue() !== '') {
//Check to see if that Data Entry Okay cell is bad and if so kick out a message to the user
if (checkDataEntry == 'bad') {
ui.alert('Email ' +rowOfInterestEmail+ ' and share that Data Entry per Form 851-1 CCA/Device Test Checklist may require your review because your submission shows:\n\nOn ' +rowOfInterestTime+ ', when you tested ' +rowOfInterestTestedWhat+ ', you declared ' +rowOfInterestFailedInspection+ ' failed inspection, ' +rowOfInterestFailedTest+ ' failed test and ' +rowOfInterestTotalAccepted+ ' were accepted but you declared that the total number tested was ' +rowOfInterestTotalTested+ '.\nWe were expecting the total number tested to be ' +rowOfInterestTotalExpected+ '.\n\nPlease resubmit the form to update these numbers at your earliest convenience--thank you.')
}
} else {
sheet.getActiveRange().autoFill(spreadsheet.getRange('J3:L' +lastRow), SpreadsheetApp.AutoFillSeries.DEFAULT_SERIES);
return rangeDataEntryOkay.getNextDataCell(SpreadsheetApp.Direction.UP).getRow();
}
}
So I'm pulling some data from GMail and adding a new row to a sheet that has a specific format. Name, Address, etc etc
On Column "P" I want to replicate the below:
=IF(NOT(ISBLANK($J3985)),"Replied", IF((TODAY()>=$O3985),"Late", "OK"))
However, I want to replace 3985 with Row(), for the row number that I'm appending, while I'm appending it. I've tried playing with: ADDRESS(row(),10) but this returns a string value that I can't seem to re-insert into a formula in a manner that works.
What I'm passing through in appendRow now:
var replied = "";
var later = x // a Date that's today + 6 weeks
var checkResult = `=IF(NOT(ISBLANK(` + replied + `)), "Replied", IF((TODAY()>=` + later + `), "Late", "OK"))`;
I want it so that I can populate the "responded" cell at a later point in the sheet and for this to still work. Would be keen to hear your suggestions for the same.
If you use appendRow:
=IF(NOT(ISBLANK(INDIRECT("RC[-6]",FALSE))),"Replied", IF((TODAY()>=INDIRECT("RC[-1]",FALSE)),"Late", "OK"))
If you use setFormulaR1C1:
Method A
Putting the row number directly with template literal
Method B
You could use setFormulaR1C1(formula)
'=IF(NOT(ISBLANK(RC[-6])), "Replied", IF((TODAY()>=RC[-1]), "Late", "OK"))';
Thanks in advance for your help. I'm new to apps script.
I have a gsheet with 98 columns and 25000 rows. All I want to do is copy 24 of the 98 columns to a new sheet.
Currently I am using filter & map, and it works:
var data = sourceSheet.getDataRange().getValues(); //read all columns & rows into array
var filterData = data.filter(function(e, j){return j > 0 && e}).map(function(e){return [e[88], e[14], e[13], e[4], e[17], e[87], e[91], e[48], e[57], e[31], e[89], e[82], e[70], e[97], e[47], e[30], e[72], e[71], e[67], e[34], e[33], e[00], e[38], e[39]]}); //extract just the columns I want into a new array
but that 2nd line takes almost an hour to execute! I presume because it is processing every element 1-by-1, even though it is just to return each one.
I haven't tried getValue'ing and setValue'ing columns one at a time because everything I read says limit external calls and do everything in memory. And I can't imagine pushing elements 1-by-1 would be faster than filtering.
Suggestions for faster execution?
function doCopy(SpreadID, OrgSheet, DestSheet, OrgRange, Sql, DestCell) {
var mySpread = SpreadsheetApp.openById(SpreadID);
var myQry = '=QUERY(' + OrgSheet + "!" + OrgRange + ',\"'+ Sql + '\")';
var myDestSheet = mySpread.getSheetByName(DestSheet);
myDestSheet.getRange(DestCell).setFormula(myQry);
}
Sample to call, but the destination sheet must be blank:
doCopy(spreadsheetId,"Sheet1", "Sheet2", "A:G", "Select A, C, F", "A1");
Another way to look at the problem which might be more time-effective and easy to implement is the following :
Duplicate the original sheet into a new one with copyTo(spreadsheet) (https://developers.google.com/apps-script/reference/spreadsheet/sheet#copytospreadsheet). You might also want to check this post for more information about creating a sheet into the same spreadsheet (Google Script to Duplicate Sheet that is Not Active)
Delete the columns you don't want to keep with deleteColumn(columnPosition) or deleteColumns(columnPosition, howMany)(see https://developers.google.com/apps-script/reference/spreadsheet/sheet#deletecolumncolumnposition).
You might also want to start deleting columns from the right side of the sheet to make the implementation easier.
Let me know if it helps.
I work for a major Motor Manufacturer and I need some help with a google sheets script that I need to write (I assume a script is the way forward?)
I have a google sheets file that is used to monitor issues and planned improvements for multiple departments. The workbook has multiple tabs that I need to somehow copy and paste data around within it.
I have included a sample file to try and help explain it, it's quite a complicated file to try and explain...I will do my best. It sounds like I need to use a script but I am new to using scripts.
Link to Sheet
Explanation of file:
On a weekly basis I update the sheet with latest data into the "Master data" sheet, this sheet feeds the "electrical" sheet using a query.The dept owner for electrical updates updates the "electricalinput" sheet with his actions and timing and this feeds into the "electrical" sheet also. The "electrical" sheet in turn feeds the "improvement data" sheet which feeds the chart.
It is arranged like this so what when new issues are added and the order changes the comments follow on the "electrical" sheet, and the owner only needs to update the "electricalinput" sheet with comments that he hasn't already done.
What I would like try and do:
I would like to be able to run a script that filters or extracts anything in the electrical sheet with #N/A against it (which means the 3 cell combination hasn't been found in the input sheet) and copy and paste just those items into the "electricalinput" sheet at the next available line that isn't populated. In the actual file there are multiple depts so variations of the script will have to run to cover the different depts.
I've updated the spreadsheet that you shared with relevant code.
You can run the script via Add Ons->Master Data Utils->Clean Up Keys menu. The menu was created in the OnOpen function from this script.
Note that there are a couple of requirements as below for the script to work.(already taken care of in the shared ss)
Departments lists and details helpful for processing all the data as shown below.
There should be a NamedRange called Department_List which would be top left cell of the table shown above.
Column to Clean -> Action Summary
is the column we would be using to search of #N/A
Display Sheet Name -> Electrical
is where the #N/A would be.
Input Sheet Name -> Electricalinput
is the sheet name that would be updated.
Copy Cols From(Disp) -> B:D
the columns that would be copied over
Similar to d but this is where sata would be copied to
But at the moment the code doesn't use this and just pastes into first available cell in col B
Here's the code I attempted, let me know of any suggestions or alterations that would make it easier to use.
function cleanComponentList(curSS) {
Logger.log("Init script cleanComponentList 'Department_List'")
curSS = curSS || SpreadsheetApp.getActiveSpreadsheet()
var deptListStart = curSS.getRangeByName("Department_List")
if(deptListStart==null){
Logger.log("This script cannot be used without the named range 'Department_List'")
return
}
var dept = deptListStart.offset(1,0)
while( ! dept.isBlank() ){
Logger.log("Started processing " + dept.getDisplayValue() + " Dept.")
//Get Department Sheet
var deptSht = curSS.getSheetByName(dept.offset(0,1).getValue())
//Get Column that has Action Summary in department sheet
var actSummCol=deptSht.getRange(2, 1)
//Get Department Input Sheet (Target Sheet where we need to copy the final values to
var deptInputSht = curSS.getSheetByName(dept.offset(0,2).getValue())
//Find column with the "Column Header"/"Summary" in which we search #N/A
while( ! (actSummCol.getDisplayValue() === dept.offset(0,3).getDisplayValue())){
var tmp = actSummCol.getDisplayValue()
var actColRng = actSummCol.getA1Notation()
actSummCol=actSummCol.offset(0,1)
}
var actSummColAddress = actSummCol.getA1Notation();
Logger.log("Found key \"" + dept.offset(0,3).getDisplayValue() + "\" for " + dept.getDisplayValue() + " # " + actSummColAddress)
var errRows = []
//Get all rows that have error Action Summary
var lastRow = deptSht.getLastRow()
for(nRow=1;nRow<lastRow;nRow++){
//There should be a better of checking #N/A
if(actSummCol.offset(nRow,0).getDisplayValue().equals("#N/A")){
errRows.push(nRow+actSummCol.getRow())
}
}
Logger.log("Got " + errRows.length + " error rows for " + dept.getDisplayValue() + " Dept.")
//Get Cell where data append should start from.
var deptInputLastAvailableRow = deptInputSht.getRange("B1")
while(!deptInputLastAvailableRow.isBlank() || deptInputLastAvailableRow.isPartOfMerge()){
deptInputLastAvailableRow=deptInputLastAvailableRow.offset(1,0)
}
Logger.log(dept.getDisplayValue() + " Dept." + " Input will be updated from " + deptInputLastAvailableRow.getA1Notation())
//Copy CDE from the filtered rows to Department Input Sheet
var srcCols=deptSht.getRange(dept.offset(0,4).getDisplayValue())
//There should be a better way of iterating. for-of throws syntax error!!!
for(idx in errRows){
var row = errRows[idx];
var lc = srcCols.getLastColumn()
var fc = srcCols.getColumn()
var errKeyRangeAddress=srcCols.getCell(row,1).getA1Notation() + ":" + srcCols.getCell(row,lc-fc + 1).getA1Notation()
var errKeyRange=deptSht.getRange(errKeyRangeAddress)
errKeyRange.copyTo(deptInputLastAvailableRow)
deptInputLastAvailableRow=deptInputLastAvailableRow.offset(1,0)
}
Logger.log("Copied " + errRows.length + " entries to Sheet \"" + deptInputSht.getName() + "\"")
Logger.log("Finished Processing " + dept.getDisplayValue() + " Dept.")
dept=dept.offset(1,0)
}
}
function onOpen(){
//Choose which way you want your menu to appear.
//SpreadsheetApp.getActiveSpreadsheet().addMenu("Monitor", [{name:"Clean Up Keys",functionName:"cleanComponentList"}])
SpreadsheetApp.getUi().createAddonMenu().addItem("Clean Up Keys", "cleanComponentList").addToUi()
}
This is a simple copy script to help you get started. copyTo is documented here. The rest of the commands can be found in the same documentation.
After you've done a few of your own scripts you'll find it a lot easier to make up your own rather than spending all of your time copying scripts.
function copyPasteSelectedRows()
{
var ss=SpreadsheetApp.getActive();
var sh1=ss.getSheetByName('Sheet2');
var sh0=ss.getSheetByName('Sheet1');
var rg0=sh0.getDataRange();
var vA=rg0.getValues();
for(var i=1;i<vA.length;i++)
{
if(vA[i][10]==1)//condition to meet to copy
{
var src=sh0.getRange(i+1,1,1,vA[i].length);//source range
var tar=sh1.getRange(sh1.getLastRow() + 1,1,1,vA[i].length);//target range
src.copyTo(tar);
}
}
}
This is Sheet1:
This is Sheet2 after the copy: