Ensure column range expands to include newly inserted rows - google-apps-script

I have a spreadsheet with 3 charts placed on it, vertically.
Each has a header (Row 1, the header for Chart 1, is frozen).
Chart 1 data is currently within the range A2 through P26.
Data from Chart 1 is summed and referenced in the bottom row of Chart 1 (A27 through P27), as well as in cells of Chart 2 and Chart 3.
I have been working on a script that, once assigned to a button, will insert a new Row 2 at the top of the sheet (beneath Row 1, since header Row 1 is frozen).
The newly inserted row maintains conditional formatting from the below Chart 1 rows, as well as 3 formulas that are needed, but its data is not included within any of the summations elsewhere on the sheet.
Is there a way to make sure that the formulas referencing A2 through P26 always include the newly inserted Row 2 each time a new Row 2 is inserted?
Here is my code:
function onOpen() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var menu = [{name: 'Insert Row', functionName: 'insertRow'}];
ss.addMenu('Insert', menu);
}
function insertRow() {
var spsh = SpreadsheetApp.getActiveSpreadsheet();
var sheet = spsh.getSheetByName('testSheet');
sheet.activate();
sheet.setFrozenRows(1);
sheet.insertRows(2);
var range = sheet.getRange('G2');
range.setFormula('=sum(E2:F2)');
var range = sheet.getRange("O2");
range.setFormula('=iF(K2="ordered", G2, "$0.00")');
var range = sheet.getRange("P2");
range.setFormula('=iF(K2="lost", G2, "$0.00")');
}

A cell reference that does not change with row insertion/deletion can be created with indirect(string):
=sum(indirect("A2"):A26)
This means the range will always begin with the cell A2, no matter what insertions happen.
Another approach
For completeness, I'll point out another way to deal with this: include row 1 in the range, so that insertion happens within the range. Depending on what the formulas are, they may already ignore the text content of the header row. If they don't, you can explicitly exclude the first row like this:
=sum(filter(A1:A26, row(A2:A26)>1))

Related

Create Unique ID from 0 to Upward Google Sheets

I have a sheet where data is being added to the last empty row and inserting the new row between the rows which has already data.
I want to create a Unique Numeric number that will be 0 to upward in sequence. Like we use =IF(B3<>"",B2+1,"").
But when new row is added the formula is missed I want to Add unique number for each row.
Because when you insert the row all values are bonded but when you type anything in newly added row in Col B all the number goes disturb. It should work like timestamp once a number is allotted to specific row data it should not change even new row is inserted within data.
For example If script has assigned number 1 to 7 and new row inserted then it should assign new row a number 8 even new row is inserted with data in col B
Your help will be appreciated
ALTERNATIVE SUGGESTION
If I have understood your goal correctly, you may try this custom script below that you can copy and paste as a bound script to your Spreadsheet file:
UPDATED Script
function onEdit() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var data = ss.getRange("B2:B").getValues().filter(String); //Add the range where data you want to have IDs, on my end, it's B2 and below (B2:B)
var ids = [];
var lastRow;
for(row=0; row<=data.length-1; row++){
ids.push([row]);
}
lastRow = ids.length + 1;
ss.getRange("A2:A"+lastRow).setValues(ids);
}
SAMPLE DEMONSTRATION
The custom function will add IDs starting on the cell A2 and below once you run it for the first time from the Apps Script editor:
When new added data on cell B10 has been entered:
In the top row,
=ARRAYFORMULA({"ID";SEQUENCE(COUNTA(B:B)+0)})
Change 0 to 1 or 2, if you want some blank row offset.

1:Update from Sheet 1 static row to MasterSheet dynamic row 2:Show & Hide columns and rows base on cell value

SHEET 1
A: I want to update changes to my "MasterSheet" with the same mobile dynamic row by clicking the cell G1 or G6 macro buttons.
https://docs.google.com/spreadsheets/d/1ISoV2q9g9L0cwP00eCfcb6k7FJ46C-E7GIOAjZCEZ3A/edit?usp=sharing
B: Hide columns and Show Columns D16
C: Hide rows and Show rows A21
For the first of your doubts, you can simply use a combinaton of getValues() and setValues() to move your data from the source to the destination creating a new row in the destination. The following piece of code has self explanatory comments and will achieve what you are aiming for (same would apply for G6):
function getDynamicRows(){
// Get source sheet
var sheet1 = SpreadsheetApp.getActive().getSheetByName('Sheet 1');
// get values of the row we want to store
var range = sheet1.getRange('B4:Q4').getValues();
// Get destination sheet
var masterSheet = SpreadsheetApp.getActive().getSheetByName('MasterSheet');
// Get range of destination sheet. If we want to append a new row at the end of
// our data we get the last row and add one as that is where the new data will go
// and then set the number of columns the same as the ones from the original source
// Finally just set values to the array of values we got from the source
masterSheet.getRange(masterSheet.getLastRow()+1,1,1,range[0].length).setValues(range);
}

Google Sheets, fill data from form sheet into specific cells on other sheets

I'm new to coding in general (other than experience with MATLAB which I don't think counts) but I'm starting with trying to code in the Google Sheets API for some advanced functionality.
The code I'm trying to write is for a spreadsheet that I track all my car expenses on. I have it doing a bunch of number crunching for MPG currently, but don't want to have to find the row and column each time to enter the date. Instead I'd like one sheet that is clean and simple that I enter the variables on (Miles driven, gallons pumped, price per gallon, estimated MPG from the car computer) and it fills the other sheets in the document with that information automatically when I hit save, then clears the form so I can do it again next time.
Here is what I have so far.
function submitData() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var formSS = ss.getSheetByName('Fill-Up'); //Form Sheet
var datasheet1 = ss.getSheetByName('Mileage Stats'); //Data Sheet 1
var datasheet2 = ss.getSheetByName('Costs and Savings'); //Data Sheet 2
//Input Values 1
var values1 = [[formSS.getRange('B3').getValue(),
formSS.getRange('B6').getValue()]];
datasheet1.getRange(datasheet1.getLastRow()+1, 2, 1, 2).setValues(values1);
//Input Values 2
var values2 = [[formSS.getRange('B4').getValue(),
formSS.getRange('B5').getValue()]];
datasheet2.getRange(datasheet2.getLastRow()+1, 2, 1, 2).setValues(values2);
}
It works with the exception of two issues that I haven't been able to solve yet.
1) It writes the information to a new row at the bottom of the page, not the next empty row.
2) It isn't writing the information in B6 to the correct cell. I want B3 written to column B in "Mileage Stats" sheet, and it is, B4 is written to column B in "Costs and Savings" as I want, and B5 is written to column C in "Costs and Savings" as I want, but B6 is written to column C in "Mileage Stats" but I want it in column G, and can't figure out how to change that with my current code, or any other code I can find.
Any help anyone can give would be awesome!
Are you using any ArrayFormulas on the sheets that you are trying to use getLastRow()? If so, ArrayFormula will add data into the cells for the whole range.
You may want to look at using the Sheet.appendRow() method like below and remove all the empty rows on your data sheets. appendRow() will add a new row at the bottom of the sheet with that data passed to the method.
datasheet1.appendRow(values1);
datasheet2.appendRow(values2);
Cells with functions are counted as data cells, that's why you're inserting the rows at the bottom of the sheet. To solve this, you can use getNextDataCell() method to a column range without functions (e.g. a cell in column B), which returns the last cell with data for a given direction. I tested the below code and worked for inserting the data in the last row with data in column B:
function submitData() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var formSS = ss.getSheetByName('Fill-Up'); //Form Sheet
var datasheet1 = ss.getSheetByName('Mileage Stats'); //Data Sheet 1
var datasheet2 = ss.getSheetByName('Costs and Savings'); //Data Sheet 2
//Input Values 1
var values1 = [[formSS.getRange('B3').getValue(),
formSS.getRange('B6').getValue()]];
var lastRowInB = datasheet1.getRange("B6").getNextDataCell(SpreadsheetApp.Direction.DOWN).getLastRow();
datasheet1.getRange(lastRowInB+1, 2, 1, 2).setValues(values1);
//Input Values 2
var values2 = [[formSS.getRange('B4').getValue(),
formSS.getRange('B5').getValue()]];
lastRowInB = datasheet1.getRange("B7").getNextDataCell(SpreadsheetApp.Direction.DOWN).getLastRow();
datasheet2.getRange(lastRowInB+1, 2, 1, 2).setValues(values2);
}

Google Sheets (Google Finance) - Get stock price once and track

I would like to, upon data being entered into a new row (from external sheet - date, time, stock ticker), use google finance to get the current price of the stock into a cell and then no longer update the price in that cell. In the cell next to it, I want to track the price and in the cell next to that, I want to track the high, since creation (the highest value of the 'current price' cell).
Here is my sheet https://docs.google.com/spreadsheets/d/1EUU1bZnIfBatNI8H9pPk202PCD1g8wWXt5M0wx6S-jM/edit?usp=sharing
All I've got so far is a high value tracker that runs on the first row only for the right cells. Embarrasingly enough I can't figure out how to apply it to the entire columns.
So in summary, date time and stock will be entered into column A B and C. When that happens, I want to get the current price of the stock in C, and have that number no longer update. In D I want the price to be tracked, like Google Finance normally functions, and in E, the highest value of the D, in the same row.
That's the goal. Any help would be very much appreciated :)
All I've got so far is a high value tracker that runs on the first row
only for the right cells. Embarrasingly enough I can't figure out how
to apply it to the entire columns.
The code bound to your spreadsheet is
function onEdit() {
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getRange("D2:E2");
var values = range.getValues()[0];
range.setValues([[values[0], Math.max(values[0], values[1])]]);
}
What does this code do?
It sets the range to work with always to D2:E2 - no matter what the actual edited range is
It works only with the first row of the range (range.getValues()[0])
It compares the 0 and 1 columns of the range (e.g. columns D and E) and assigns the value of column D back to column D (is it necessary?) and the higher of the two values to column E
How to modify your code?
It is not quite clear from your description how column D is populated and what you want to do with column F, but to give your general advice:
If you want your function to run on all rows:
Modify your range and expand it until the last row. Subsequently loop
through all rows:
function onEdit() {
var sheet = SpreadsheetApp.getActiveSheet();
var lastRow = sheet.getLastRow();
var range = sheet.getRange("D2:E"+lastRow);
for (var i = 0; i < lastRow-1; i++){
var values = range.getValues()[i];
range.setValues([[values[0], Math.max(values[0], values[1])]]);
}
}
If you want you code to run only on the currently edited row:
Use the event object e.range to find out which is the edited row and work on this row:
function onEdit(e) {
var sheet = SpreadsheetApp.getActiveSheet();
var range = e.range;
var row = range.getRow();
var values = sheet.getRange(row, 4, 1, 2).getValues()[0];
range.setValues([[values[0], Math.max(values[0], values[1])]]);
}
Note: getRange(row, 4, 1, 2) is the notation to get the range starting with the defined row and column 4 (D), 1 row long and two columns wide, see here.
IMPORTANT: If your sheet is being populated automatically from an
external sheet - the onEdit trigger will not work for you (it only
fires on manual, human-made edits). In this case you will need a
workaround as described here.

Google Apps Script - How to copy a column(s) to another sheet at next available column

I have a requirement where I need to take values from one column (or more) and copy them to another sheet in the next available column (s). I have written a script like this. It does copy the values from one column but it has no way to move forward for taking another snapshot of new data in same source column to destination sheet's next free column.
*//keep a copy of Sales numbers for every month
function readSalesNum() {
var sheetFrom = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sales plan");
var sheetTo = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SalesRecordsMonthly");
// Copy from 17th row, 4th column, all rows for one column
var rangeToCopy = sheetFrom.getRange(17, 4, sheetFrom.getMaxRows(), 1);
//Paste to another sheet from first cell onwards
rangeToCopy.copyTo(sheetTo.getRange(1, 1));
}*
I am sorry about the poor formatting :( I need to modify this script to mention any set of columns from source sheet and copy them to destination sheet. And for new month, it should do the same in next set of columns, instead of overwriting as it does now. Also, the copy should happen only for values which is missing yet. I know there's an option of ContentOnly in script but not sure how to use it.
If I understood correctly, here is a code that does what you wanted, ie get the values from one sheet in column4 from row 17 and copy it to the other sheet without overwriting to columns starting at row 1
function readSalesNum() {
var sheetFrom = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sales plan");
var sheetTo = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SalesRecordsMonthly");
// Copy from 17th row, 4th column, all rows for one column
var valuesToCopy = sheetFrom.getRange(17, 4, sheetFrom.getLastRow(), 1).getValues();
//Paste to another sheet from first cell onwards
sheetTo.getRange(1,sheetTo.getLastColumn()+1,valuesToCopy.length,1).setValues(valuesToCopy);
}
test sheet here, make a copy to run the script - view only
This covers only the first part of your question, the second part was a bit confusing (as mentioned in my comment above).
The entire 4th column & 17th row onward can be appended as a column into a different sheet with this approach also.
var sheetfrom = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('sheetfrom');
var sheetto = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('sheetto');
sheetfrom.getRange(17, 4, sheetfrom.getLastRow(), 1).copyTo(sheetto.getRange(1,
sheetTo.getLastColumn()+1, sheetfrom.getLastRow()-17, 1), {
contentsOnly: true
});
This also overcomes the empty cell problem of copying data from some Formula outputs.
Try this
*//keep a copy of Sales numbers for every month
function readSalesNum() {
var sheetFrom = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sales plan");
var sheetTo = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SalesRecordsMonthly");
// Copy from 17th row, 4th column, all rows for one column
var rangeValues = sheetFrom.getRange(17, 4, 1, sheetFrom.getMaxRows()).getValues();
//Paste to another sheet from first cell onwards
sheetTo.appendRow(rangeValues[0]);
}
This will do what you want with the range you suggested. However, looks like you're trying to get two different ranges so you'll have to accommodate that.