I created a simple form in google sheets that adds my inputs from Cells B1-B12 to a table. It works perfectly, except instead of adding my entry to the next blank row, it adds it to row 501. The reason for this is because several columns include formulas that go down to row 500, so it seems getlastrow() is taking into consideration all cells that include no values, but formulas.
I think this can be solved by either:
1.) Specifying getlastrow() starting from column G, which doesn't include any formula fields
2.) Somehow ignoring formula fields when using the getlastrow() function
I've exhausted my google/stack overflow searches, so if anyone has any ideas on how I can tweak my code to accomplish 1 or 2, I would very much appreciate it!
// Clear Form
function ClearCell() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var formS =ss.getSheetByName("Copy of Sold"); //FormSheet
var rangesToClear = ["B1","B2","B3","B4","B5","B6","B7","B8","B9","B10","B11","B12"];
for (var i=0; i<rangesToClear.length; i++) {
formS.getRange(rangesToClear[i]).clearContent();
}
}
//---------------------------------------------------------
function SubmitData(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var formS = ss.getSheetByName("Copy of Sold"); //Data Entry Sheet
var dataS = ss.getSheetByName("Copy of Sold"); //Data Entry Sheet / Data
var values = [[formS.getRange("B1").getValue(),
formS.getRange("B2").getValue(),
formS.getRange("B3").getValue(),
formS.getRange("B4").getValue(),
formS.getRange("B5").getValue(),
formS.getRange("B6").getValue(),
formS.getRange("B7").getValue(),
formS.getRange("B8").getValue(),
formS.getRange("B9").getValue(),
formS.getRange("B10").getValue(),
formS.getRange("B11").getValue(),
formS.getRange("B12").getValue()]];
dataS.getRange(dataS.getLastRow()+1,7,1,12).setValues(values);
ClearCell()
}
I believe your goal as follows.
You want to retrieve the values from the cells "B1:B12" and want to put the next row of the last row of the column "G" from the column "G" to the column direction. And, you want to clear the cells of "B1:B12".
Modification points:
At ClearCell(), I think that the process cost might be able to be reduced a little using the range list.
At SubmitData(), I think that the values of cells "B1:B12" can be retrieved by one call of getValues.
getLastRow returns the last row of data range including the values and formulas. It seems that this is the current specification.
In this modification, the last row of the column "G" is retrieved using isBlank().
ss.getSheetByName("Copy of Sold") can be used one time.
When above points are reflected to your script, it becomes as follows.
Modified script:
function ClearCell(formS) {
var rangesToClear = ["B1", "B2", "B3", "B4", "B5", "B6", "B7", "B8", "B9", "B10", "B11", "B12"];
formS.getRangeList(rangesToClear).clearContent();
}
function SubmitData() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var formS = ss.getSheetByName("Copy of Sold");
var values = formS.getRange("B1:B12").getValues().flat();
for (var r = formS.getLastRow(); r >= 1; r--) {
if (!formS.getRange("G" + r).isBlank()) {
formS.getRange(r + 1, 7, 1, 12).setValues([values]);
break;
}
}
ClearCell(formS);
}
References:
getRangeList(a1Notations)
getLastRow()
isBlank()
function SubmitData(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var formS = ss.getSheetByName("Sold"); //Data Entry Sheet
var dataS = ss.getSheetByName("Sold"); //Data Entry Sheet / Data
var values = [[formS.getRange("B1").getValue(),
formS.getRange("B2").getValue(),
formS.getRange("B3").getValue(),
formS.getRange("B4").getValue(),
formS.getRange("B5").getValue(),
formS.getRange("B6").getValue(),
formS.getRange("B7").getValue(),
formS.getRange("B8").getValue(),
formS.getRange("B9").getValue(),
formS.getRange("B10").getValue(),
formS.getRange("B11").getValue(),
formS.getRange("B12").getValue()]];
dataS.getRange(getLastRowByDirectionUp(dataS, "G")+1,7,1,12).setValues(values);
ClearCell();
}
function getLastRowByDirectionUp(sheet, col) {
if(sheet.getRange(col + sheet.getLastRow()).getValue()=="") {
return sheet.getRange(col + sheet.getLastRow()).getNextDataCell(SpreadsheetApp.Direction.UP).getRow();
}
else {
return sheet.getLastRow();
}
} function SubmitData(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var formS = ss.getSheetByName("Sold"); //Data Entry Sheet
var dataS = ss.getSheetByName("Sold"); //Data Entry Sheet / Data
var values = [[formS.getRange("B1").getValue(),
formS.getRange("B2").getValue(),
formS.getRange("B3").getValue(),
formS.getRange("B4").getValue(),
formS.getRange("B5").getValue(),
formS.getRange("B6").getValue(),
formS.getRange("B7").getValue(),
formS.getRange("B8").getValue(),
formS.getRange("B9").getValue(),
formS.getRange("B10").getValue(),
formS.getRange("B11").getValue(),
formS.getRange("B12").getValue()]];
dataS.getRange(getLastRowByDirectionUp(dataS, "G")+1,7,1,12).setValues(values);
ClearCell();
}
function getLastRowByDirectionUp(sheet, col) {
if(sheet.getRange(col + sheet.getLastRow()).getValue()=="") {
return sheet.getRange(col + sheet.getLastRow()).getNextDataCell(SpreadsheetApp.Direction.UP).getRow();
}
else {
return sheet.getLastRow();
}
}
Related
I am trying to accomplish 2 things with Google Sheets. I have a Main sheet with data and I would like to:
Create new sheet with the name based on a cell value in Column B (accomplished)
Copy the rows containing those values in B in that new sheet
Part 1 works fine with this script:
function onOpen() {
var menu = [{
name : "Add",
functionName : "newSheet"
}
];
SpreadsheetApp.getActiveSpreadsheet().addMenu("Sheet", menu);
}
function newSheet() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var templateSheet = ss.getActiveSheet();
var sheet1 = ss.getSheetByName("Sheet1")
var getNames = sheet1.getRange("B2:B").getValues().filter(String).toString().split(",");
for (var i = 0; i < getNames.length; i++) {
var copy = ss.getSheetByName(getNames[i]);
if (copy) {
Logger.log("Sheet already exists");
} else {
templateSheet.copyTo(ss).setName(getNames[i]);
ss.setActiveSheet(ss.getSheetByName(getNames[i]));
ss.moveActiveSheet(ss.getNumSheets());
}
}
}
The problem is when new sheets are created it copies the content of the main sheet. I would like to have only the rows containing the value in Column B copied into the new sheet.
Instead of using copyTo you might use one of the insertSheet methods of Class Spreadsheet, then copy rows having the required value in column B into the new sheet.
The specific code to copy the rows depends on what really need to copy (values, displayed values, formulas, cells formatting, rich text cell content formatting, notes, data validation, conditional formatting)
Let say that you are only interested in passing the values, you could use something like the following in the getNames for loop:
var data = sheet1.getDataRange().getValues().filter(row => row[1] === getNames[i])
var newSheet = ss.insertSheet(getNames[i]);
if( data.length > 0 ) newSheet.getRange(1,1,data.length,data[0].length).setValues(data);
Related
insertSheet advanced options
check for existence of a sheet in google spreadsheets
You can just add one more function to remove redundant rows from any sheet. Something like this:
function main() {
var sheet = SpreadsheetApp.getActiveSheet()
remove_rows("aaa", sheet); // remove all rows, that contain 'aaa' in first column
}
function remove_rows(value, sheet) {
var data = sheet.getDataRange().getValues();
var new_data = data.filter(x => x[0].indexOf(value)>=0)
sheet.getDataRange().clearContent();
sheet.getRange(1,1,new_data.length,new_data[0].length).setValues(new_data);
}
I am trying to create a data entry form that submits data to a data sheets first open row. The problem is that the data sheet has formula in one of the columns so it is not truly empty. This is causing the current script to take the cells with formula into consideration and only selecting the rows after it.
Could you guys please assist me with a workaround to the issue.
Current script looks like this:
function submitData() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var formSS = ss.getSheetByName("FORM"); //Form Sheet
var datasheet = ss.getSheetByName("DATA"); //Data Sheet
//Input Values
var values = [[formSS.getRange("D4").getValue(),
formSS.getRange("D8").getValue(),
formSS.getRange("D12").getValue(),
formSS.getRange("D16").getValue(),
formSS.getRange("D20").getValue(),
formSS.getRange("D24").getValue(),
formSS.getRange("D28").getValue(),
formSS.getRange("L32").getValue()]];
datasheet.getRange(datasheet.getLastRow()+1, 1, 1, 8).setValues(values);
}
You can find the first free row by evaluating the row contents
Sample
var freeRow;
var columnI = datasheet.getRange("I1:I" + datasheet.getLastRow()).getDisplayValues().flat();
for(var i = 0; i < columnI.length; i++){
if(columnI[i] == "") {
freeRow = i + 1;
break;
}
}
datasheet.getRange(freeRow, 1, 1, 8).setValues(values);
In addition, you are using a Form submit trigger, you use event objects
Sample
function submitData(e) {
var range = e.range;
var row = range.getRow();
// this is the row into which the latest form response has been inserted - do with it what you need
...
}
I've created a spreadsheet that I'd like to separate based on the data found in one particular cell. The main spreadsheet has a column that indicates either "tardy, discipline, reinforcement." I'd like to move "tardy" and "reinforcement" into their own spreadsheets. Additionally, I'd like "tardy" and "reinforcement" lines to be removed, once in the appropriate sheet. This should give me a total of three spreadsheets.
I've used a script before that was working and moving my tardy lines onto a different spreadsheet. However, for whatever reason, I am not getting an error that says "TypeError: Cannot read property 'length' of undefined (line 19, file "Code").
I've been playing around with this and it's likely I've messed something up. I have no experience coding but I'm trying my best to figure this out. Currently, the script I have is below. Any help would be greatly appreciated.
function copyrange() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Minor Infractions - Current'); //source sheet
var testrange = sheet.getRange('O:O'); //range to check
var testvalue = (testrange.getValues());
var csh = ss.getSheetByName('Tardies - Current'); //destination sheet
var data = [];
var j =[];
//Condition check in O:O; If true copy the same row to data array
for (i=0; i<testvalue.length;i++) {
if ( testvalue[i] == 'Tardy') {
data.push.apply(data,sheet.getRange(i+1,1,1,25).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 the 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;
}
}
}
as this answer says, you could accomplish this with a function:
=sort(importrange("spreadsheetURL", "Sheet1!A2:AA10000"),sort_col#,TRUE/FALSE,[sort_col2#],[TRUE/FALSE]...)
keep in mind this function is a guide, you can't just copy and paste, you have to fill out the values
Try this:
function copyrange() {
const ss=SpreadsheetApp.getActive();
const sheet=ss.getSheetByName('Minor Infractions - Current');
const testrange=sheet.getRange(1,15,sheet.getLastRow());
const testvalue=testrange.getValues();
const csh=ss.getSheetByName('Tardies - Current');
let data=[];
let d=0;
for (let i=0;i<testvalue.length;i++) {
if (testvalue[i]=='Tardy') {
data.push(sheet.getRange(i+1,1,1,25).getValue());
sheet.deleteRow(i+1-d++);
}
}
csh.getRange(csh.getLastRow()+1,1,data.length,data[0].length).setValues(data);
}
I have a list of tasks on a sheet, and I've been trying to find a simple way to move the rows with tasks to a different sheet depending on the status in one column (e.g. blank, done, paid). For instance, a task on row 2 is marked "done". I then need that row to be deleted and moved to the sheet labeled "done". From there, when the task is paid, i need that row deleted from the "done tab to the "paid" tab.
I've found some code that allows me to do this once for 1 row, but it doesn't repeat for the rows below it. Here is the code I've been using:
function ConditionalShimmy2(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = ss.getSheetByName('not done');
var sheet2 = ss.getSheetByName('done');
var sheet3 = ss.getSheetByName('paid');
var cell = sheet1.getRange("f2:f277").getCell(1, 1);
var rowtoInsert = "2:277";
if(cell.getValue() == "done")
{
sheet2.insertRows(2);
var range2 = sheet2.getRange(2 ,1,1,sheet1.getLastColumn());
sheet1.getRange(cell.getRow(),1, 1, sheet1.getLastColumn()).copyTo(range2);
sheet1.deleteRow(cell.getRow());
}
}
Can anyone help me finish this code and let me know where I'm going wrong?
In order to achieve your task, you can try the below proposed solution that will suit your situation.
Snippet
function ConditionalShimmy2() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var main = ss.getSheetByName("THE_NAME_OF_THE_MAIN_SHEET");
var sheet1 = ss.getSheetByName("not done");
var sheet2 = ss.getSheetByName("done");
var sheet3 = ss.getSheetByName("paid");
var taskNames = main.getRange(row, column, numRows, numColumns).getValues();
var taskStatus = main.getRange(row, column, numRows, numColumns).getValues();
var rowsDeleted = 0;
for (var i = 0; i < taskStatus.length; i++) {
if (taskStatus[i][0] == "not done") {
sheet1.appendRow([taskNames[i][0]]);
main.deleteRow((i + 1) - rowsDeleted);
rowsDeleted++;
} else if (taskStatus[i][0] == "done") {
sheet2.appendRow([taskNames[i][0]]);
main.deleteRow((i + 1) - rowsDeleted);
rowsDeleted++;
} else if (taskStatus[i][0] == "paid") {
sheet3.appendRow([taskNames[i][0]]);
main.deleteRow((i + 1) - rowsDeleted);
rowsDeleted++;
}
}
}
Explanation
The above snippet loops through all the tasks and their associated statuses and based on the status column, each row is then appended to the appropriate sheet by making use of the appendRow() method and then deleted from the main sheet by making use of the deleteRow() method.
Note
In order to make the above code snippet work for your situation, you will have to customize the parameters for the getRange method associated to both taskNames and taskStatus. Therefore, the row, column, numRows, numColumns parameters represent the range with the top left cell at the given coordinates with the given number of rows and columns.
Reference
Sheet Class - getRange();
Sheet Class - appenRow();
Sheet Class - deleteRow().
If I want my Google Apps Script app get the current selected row in a spreadsheet, I use something like this:
function getCurrentRow() {
var currentRow = SpreadsheetApp.getActiveSheet().getActiveSelection().getRowIndex();
return currentRow;
}
But what if I want to get the first cell in this row (to put a comment in it), how do I proceed?
You can use offset to get a range relative to the active range.
function getFirstCellInRow() {
var sheet = SpreadsheetApp.getActiveSheet();
var activeCell = sheet.getActiveCell();
var firstCell = activeCell.offset(0, 1-activeCell.getColumn());
return firstCell;
}
Or just use getRange(A1notation), starting with what you had:
function getFirstCellInRow2() {
var sheet = SpreadsheetApp.getActiveSheet();
var currentRow = sheet.getActiveSelection().getRowIndex();
return sheet.getRange("A"+currentRow);
}
To write into the first cell in the row:
getFirstCellInRow().setValue('Tada');
There is a getCell() method you can use ( https://developers.google.com/apps-script/reference/spreadsheet/range#getCell(Integer,Integer) )
function getFirstCell() {
var firstCell= SpreadsheetApp.getActiveSheet().getActiveSelection().getCell(1,1);
return firstCell;
}
The following script will insert "My Info" in the first row available in column A.
function FirstCellColumnA() {
// replace with destination ID
var s = SpreadsheetApp.openById('spreadsheetID');
// replace with destination Sheet tab name
var ss = s.getSheetByName('sheetname');
// In Column A first ROW Available ss.getLastRow()+1 = find the
// first row available, 1 = start in column 1, 1 = rows to start,
// 1 = column where to put the data.
ss.getRange(ss.getLastRow() + 1, 1, 1, 1).setValue('My Info');
}
I hope this help you.