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.
Related
I am very new to javascript and have searched around a ton for this and can't seem to find the issue with my code. I am attempting to simply write a code that will copy the values in a column from a pivot table sheet in Google Sheet and then paste the values in another sheet. However, before pasting the values, I want each individual value to be duplicated 12 times (for 12 months). So, assuming I have 10 unique values (A, B, C, D, E, F, G, H, I, J) that I am copying, I want to return value A 12 times in a row, then value B 12 times in a row, etc.
I run getValues, which seems to put the values in a 2 dimensional array. I've then taken this temp_array that I had created and used a for loop to duplicate each value 12 times in a new array.
However, when I setValues, I am pasting the values in my spreadsheet correctly, but I get this error message regardless (The number of columns in the data does not match the number of columns in the range. The data has 0 but the range has 1.), any ideas why?
Here is a small example of what my input could look like (1st image) and what I would want the output to look like (2nd image)
function test2() {
// activating current spreadsheet for use
var spreadsheet = SpreadsheetApp.getActive();
//empty array
var array_dept_temp = [];
// returns cell position (ex: C5) of the last row of the pivot table 1 sheet that has content in column 1
var last_row = spreadsheet.getSheetByName("Pivot Table 1").getRange("A:A").getNextDataCell(SpreadsheetApp.Direction.DOWN).getRowIndex();
//subtracting 1 from last row because we are excluding the headers. This gives us our row_length
var row_length = last_row - 1
var array_dept = [[]]
array_dept = new Array(row_length*12)
//new Array(row_length*12);
// Get value in pivot table 1 from range of row 2 (dept name, but exclude the header), column 1, all the way to last row
// Then paste it in sheet5 from row 1, column 3, all the way to the last row defined above
array_dept_temp = spreadsheet.getSheetByName("Pivot Table 1").getRange(2,1, last_row).getValues();
for (var i = 1; i < row_length; i++ )
{
//get value and then paste it in a destination
array_dept.fill(array_dept_temp[i-1], (-12 + (12*i)) , 12*i);
}
var destination_dept = spreadsheet.getSheetByName("Sheet5").getRange(2,3,row_length*12);
destination_dept.setValues(array_dept);
}
Suggestion / Alternate solution:
Try:
function test() {
var spreadsheet = SpreadsheetApp.getActive();
var sheet = spreadsheet.getSheetByName("Pivot Table 1");
var array_dept_temp = sheet.getRange(2,1, sheet.getLastRow()-1).getValues();
var array_dept = [];
for (var i = 0; i < array_dept_temp.length; i++) {
array_dept = [...array_dept, ...Array.apply(null, Array(12)).map(function(){return array_dept_temp[i]})]
}
var destination_dept = spreadsheet.getSheetByName("Sheet5").getRange(2,3,array_dept.length);
destination_dept.setValues(array_dept);
}
Result:
Another way without using fill or from.
Also some modification, you can just use .getLastRow() function to get the last row, however take not that if there is data below it will count all the rows including the blank until the row that has data. And you may also use .length on your data to setValue.
From your showing sample input and output situations, how about the following modified script?
Modified script:
function test2_sample() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var srcSheet = ss.getSheetByName("Pivot Table 1");
var dstSheet = ss.getSheetByName("Sheet5");
var srcValues = srcSheet.getRange("A2:A" + srcSheet.getLastRow()).getValues();
var dstValues = srcValues.flatMap(a => Array(12).fill(a));
dstSheet.getRange(2, 3, dstValues.length).setValues(dstValues);
}
When this script is run using your sample input sheet, I think that your expected output values are obtained.
Now, I thought that var dstValues = srcValues.flatMap(([a]) => Array(12).fill(a).map(e => [e])); can be modified to var dstValues = srcValues.flatMap(a => Array(12).fill(a));. This is simpler.
From your reply of Are you able to explain what this does? var dstValues = srcValues.flatMap(([a]) => Array(12).fill(a).map(e => [e]));, in this script, var dstValues = srcValues.flatMap(([a]) => Array(12).fill(a).map(e => [e])); can be also modified as follows. I thought that this might also help to understand it.
function test2_sample() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var srcSheet = ss.getSheetByName("Pivot Table 1");
var dstSheet = ss.getSheetByName("Sheet5");
var srcValues = srcSheet.getRange("A2:A" + srcSheet.getLastRow()).getValues();
var dstValues = [];
for (var i = 0; i < srcValues.length; i++) {
dstValues = dstValues.concat(Array(12).fill(srcValues[i]));
}
dstSheet.getRange(2, 3, dstValues.length).setValues(dstValues);
}
Note:
As additional information, when your showing script is modified, how about the following modification? In your script, I thought that it is required to add the values to array_dept in the loop. And, it is required to flatten the elements in the array.
function test2() {
var spreadsheet = SpreadsheetApp.getActive();
var array_dept_temp = [];
var last_row = spreadsheet.getSheetByName("Pivot Table 1").getRange("A:A").getNextDataCell(SpreadsheetApp.Direction.DOWN).getRowIndex();
var row_length = last_row - 1
var array_dept = []
array_dept_temp = spreadsheet.getSheetByName("Pivot Table 1").getRange(2, 1, last_row).getValues();
for (var i = 0; i < row_length; i++) {
array_dept = [...array_dept, ...Array(12).fill(array_dept_temp[i])];
}
var destination_dept = spreadsheet.getSheetByName("Sheet5").getRange(2, 3, array_dept.length);
destination_dept.setValues(array_dept);
}
Reference:
flatMap()
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();
}
}
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 have poked around and found the following code that will advance to the last line on data in our Google Spreadsheet- at least until we add more lines beyond row 297.
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var range = ss.getSheets()[0].getRange("B297");
SpreadsheetApp.setActiveRange(range);
}
I am trying to figure out how to write this script so that it will go to the last line of data, regardless of the line number.
Is there a change that I can make to this code to accomplish this?
The method getLastRow() tells you the last row of a sheet that has data. This can be used to move the selection to that row. Another thing I changed in the sheet selection: your script always operates on the first sheet; it makes more sense to operate on whichever sheet is active currently.
function myFunction() {
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getRange(sheet.getLastRow(), 1);
SpreadsheetApp.setActiveRange(range);
}
This can be placed into the spreadsheet menu using onOpen.
By the way, pressing Ctrl + ArrowDown does the same thing, if you do it in a column that has some data in every row (like the date or Id column).
The script below allows you to go to the last cell with the content of column A. It works even if some cells in the column A contain formulas.
Modifying the number in parentheses in lastRowOfCol(1) allows you to reach the last cell with content from another column.
Additionally, you can also change the focus to the first empty cell after the last one with content.
function onOpen(){
lastRowOfCol(1); //Enter the column number you want to use as the base of the search
}
function lastRowOfCol(column){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var total = sheet.getMaxRows();
var values = SpreadsheetApp.getActiveSheet().getRange(1,column,total).getValues();
for(var i=total-1;values[i]=="" && i>0; i--){}
var last = sheet.getRange(i+1,column);
//var last = sheet.getRange(i+1,1); //Option to fetch the last row of a given column, but to position in column 1
//var last = sheet.getRange(i+2,column); //Option to go to the cell below the last one with content
sheet.setActiveSelection(last);
}
Script from Marcelo Camargo in this forum
The currently relevant option that works on all non-hidden sheets, and not just on the active one:
function onOpen() {
const ss = SpreadsheetApp.getActiveSpreadsheet()
const sheets = ss.getSheets()
const properties = PropertiesService.getDocumentProperties()
const lastActiveSheetName = properties.getProperty("lastActiveSheetName")
let lastActiveSheet
for (let sheet of sheets) {
if (!sheet.isSheetHidden()) {
const sheetName = sheet.getName()
const lastEdit = properties.getProperty(sheetName)
if (lastEdit) {
if (sheetName !== lastActiveSheetName){
sheet.getLastRow() // Without this magic does not work - I could not figure out the reasons
sheet.getLastColumn() // Without this magic does not work - I could not figure out the reasons
const [lastRow, lastCol] = lastEdit.split(',')
sheet.getRange(Number(lastRow), Number(lastCol)).activate() // With focus set to this cell
//sheet.setActiveSelection(sheet.getRange(Number(lastRow), Number(lastCol))) // Without setting focus to this cell
}
else {
lastActiveSheet = sheet
}
}
}
}
if(lastActiveSheet){
lastActiveSheet.getLastRow()
lastActiveSheet.getLastColumn()
const [lastRow, lastCol] = properties.getProperty(lastActiveSheetName).split(',')
lastActiveSheet.getRange(Number(lastRow), Number(lastCol)).activate()
}
}
function onEdit() {
const ss = SpreadsheetApp.getActiveSpreadsheet()
const sheet = ss.getActiveSheet()
if (!sheet.isSheetHidden()) {
const cell = ss.getActiveCell()
const row = cell.getRow()
const column = cell.getColumn()
if (row !== 1 || column !== 1) { // Protection from the evil magic of "self-editing" the first cell
const sheetName = sheet.getName()
PropertiesService.getDocumentProperties().setProperty(sheetName, `${row},${column}`)
PropertiesService.getDocumentProperties().setProperty("lastActiveSheetName", sheetName)
}
}
}
PS: Please note that in the code I do not use a semicolon separator - it's more convenient for me.
I have this simple script:
function myFunction() {
var ssh = SpreadsheetApp.getActiveSpreadsheet();
var ss = ssh.getActiveSheet();
var s = ss.getActiveRange();
var rowIndex = s.getRowIndex();
var colIndex = s.getColumnIndex();
// Get the number of columns in the active sheet.
var colNumber = ss.getLastColumn();
if (colIndex == 1 && rowIndex != 1) {
//Get User Name from inputBox
var name = Browser.inputBox('Owner', 'Enter your Name', Browser.Buttons.OK_CANCEL);
var r1 = ss.getActiveRange().getRow();
//Insert the Name in the active row in the column 6
ss.getRange(r1, 6).setValue(name)
//Here I have other part of the code but is for copy rows to other sheet.
}
}
In column 6 have active data validation (not permit insert values that are not in the list of items).
Example: Charles, Oscar, Paul, Other. If I enter the names all in lower case do not enter. If the value entered is identical as "Charles" the value is entered.
Now, is possible these values may appear as a drop down list in the inputbox? I mean, the user can select from that list and then press Enter or Ok and the value insert into the cell.
Note: This is not a form. This is the spreadsheet.
Thanks,
UPDATED: 01/09/2014
function Historic(e) { //CopyRows
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = ss.getActiveSheet();
if (s.getName() == 'ISP1'){
var r = SpreadsheetApp.getActiveRange();
// Get the row and column of the active cell.
var rowIndex = r.getRowIndex();
var colIndex = r.getColumnIndex();
// Get the number of columns in the active sheet.
var colNumber = s.getLastColumn();
// Move row based on criteria in column 1, and if row is not the header.
if (colIndex == 1 && rowIndex != 1) { // 1 Is the Comment Column
//Call the Function Menu List
showNamePicker();
//Get values for the active row bases on the criteria.
var status = s.getRange(rowIndex, colIndex).getValue();
// --------------- Copy ROW only when someone modify the Column1 --------------------------
// Do nothing if criteria value is not actually changed to something else.
if (s.getName() != 'Historic') {
// The target sheet is the one with the same name as the criteria value.
var targetSheet = ss.getSheetByName('Historic');
var target = targetSheet.getRange(targetSheet.getLastRow() + 1, 1);
//=====Copy the Row from ISP1 to Historic=======
s.getRange(rowIndex, 1, 1, colNumber).copyTo(target);
}
}
}
}
function showNamePicker() {
var app = UiApp.createApplication().setHeight(100).setWidth(180);
var handler = app.createServerHandler('setName');
var list = app.createListBox().setName('list').addChangeHandler(handler);
var names = ['Choose a name here','Charles', 'Oscar', 'Paul', 'Other'];
for(var n in names){
list.addItem(names[n]);
}
handler.addCallbackElement(list);
app.add(list);
SpreadsheetApp.getActive().show(app);
}
function setName(e){
var ssh = SpreadsheetApp.getActiveSpreadsheet();
var ss = ssh.getActiveSheet();
var r1 = ss.getActiveRange().getRow();
ss.getRange(r1, 6).setValue(e.parameter.list);
}
Function Historic: If someone modify the Column 1, the script will copy all data in the row that was modify to historic.
Needed:
The script should be insert the name in the column 6 for that row before the script copy the row to the historic.
Issue:
When someone modify the column 1 the user press ENTER and the new active row change because is not the same and the script not insert the name in the row that was modify.
For example:
I modify the A2 and press ENTER the new row is A3, and the script will insert the information into F3 and not in F2.
I tried to call the function before this line:
//Get values for the active row bases on the criteria.
var status = s.getRange(rowIndex, colIndex).getValue();
but I'm still newbie... I this is very easy but I can't get with the solution.
I will appreciate if you can help me.
EXAMPLE:
Change Cell A2 = ID#12578
The script should be insert the name in the column F2 and then copy all row include the F2 in the sheet called Historic. (The script is not inserting the name into F2 is inserting in F3
You will have to replace you Browser.inputBox with a dialog box that will hold a list box with the desired values.
You can build this dialog box either using UiApp or HTML service.
The problem will be that nothing will prevent to enter a value directly in the cell but it was already the case in your code...
You can store the list values wherever you want, in a hidden column, another sheet, another spreadsheet or in ScriptProperties or in the code itself... that's a matter of choice :-)
Edit : a small example using UiApp :
function showNamePicker() {
var app = UiApp.createApplication().setHeight(100).setWidth(120);
var handler = app.createServerHandler('setName');
var list = app.createListBox().setName('list').addChangeHandler(handler);
var names = ['Choose a name here','Charles', 'Oscar', 'Paul', 'Other'];
for(var n in names){
list.addItem(names[n]);
}
handler.addCallbackElement(list);
app.add(list);
SpreadsheetApp.getActive().show(app);
}
function setName(e){
var ssh = SpreadsheetApp.getActiveSpreadsheet();
var ss = ssh.getActiveSheet();
var r1 = ss.getActiveRange().getRow();
ss.getRange(r1-1, 6).setValue(e.parameter.list);
return UiApp.getActiveApplication().close();// close the Ui
}