Google app script to copy column values to a different sheet - google-apps-script

I am trying to write a google app script so I can copy a few columns (C and D) from my main report sheet to a different sheet, based on the value of column M if that's equal to 0.
As shown in the pic below:
So I want to copy the "Name" and "Variety" columns into another sheet where the "Germination %" column is 0.
It can be a time based trigger, that runs after every 2 hours or onEdit also. I was trying to make it a script using OnEdit, as shown below:
function onEdit( e ){
var sheetFrom = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("germination");
var sheetTo = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("watering");
var data = sheetFrom.getDataRange().getValues();
var target = new Array();// this is a new array to collect data
for(n=0;n<data.length;++n){ // iterate in the array, row by row
if (data[n][12]=="0"){ // if condition is true copy the whole row to target
target.push(data[n]);// copy the whole row
}//if
}//for
//Paste to another sheet from first cell onwards
sheetTo.getRange(1,1,target.length,2).setValues(target);
}
But I am getting errors on this "Incorrect range width, was 15 but should be 2 (line 25, file "Code")"
Line 25 is "sheetTo.getRange(1,1,target.length,2).setValues(target)"
Thanks in advance.

In the getRange(1,1,target.length,2) method you're specifying it to select only 2 columns, change it to getRange(1,1,target.length,target[0].length) to select the array length of columns.
Update:
To write only the C and D, change the target push() to:
target.push( [[data[n][2], data[n][3]] )

Related

Generate a number of rows based on input and adding SUM formula to last row

I am trying to add a rent roll to my Google sheets real estate investment calculator. Since each property this is used for will have a different number of units, I want to generate a row for each rental unit in the property based on a user input.
For example: User inputs "12" for number of units, and 12 rows are generated that are numbered 1-12.
This will be in column A. In column B, the user will then input the amount of rent collected for each unit. The total rent from the generated rows will be added and displayed in a specific cell that can be referenced in another formula.
Here's an example https://docs.google.com/spreadsheets/d/1KCeyLCcQZPBwzuuq8v8nn0flOlIx89-Tyjv5GjR4koI/edit?usp=sharing
Anyone have a suggestion on how to do this?
You can use this sample onEdit simple trigger:
function onEdit(e) {
var sheet = e.source.getActiveSheet();
var cell = e.range;
//Check if modified cell is Sheet1!B3
if(sheet.getName() == 'Sheet1' && cell.getA1Notation() == 'B3'){
//Change formula in Sheet1!B4
var unit = cell.getValue();
var formula = '=sum(B8:B'+(8+unit-1)+')';
Logger.log(formula);
sheet.getRange('B4').setFormula(formula);
//Clear output in Sheet1 starting from row 8 for Column A and B (including format)
sheet.getRange(8,1,sheet.getLastRow(),2).clear();
//Create an output data
var output = [];
for(var i=0; i<unit; i++){
output.push([i+1,""]);
}
output.push(["Total",formula]);
Logger.log(output);
sheet.getRange(8,1,output.length,output[0].length).setValues(output);
var yellow = '#ffff00';
var orange = '#ff9900';
//Add background color 'orange' to column A
sheet.getRange(8,1,unit,1).setBackgroundColor(orange);
//Add background color 'yellow' to column B
sheet.getRange(8,2,unit,1).setBackgroundColor(yellow);
}
}
What it does?
Get the active sheet and the modified cell using the Google Sheets event objects e.source and e.range
Check if the modified cell is in Sheet1!B3
Get the value of the modified cell
Create a SUM formula based on the input value
Set the formula in Sheet1!B4
Clear the content of Sheet1 for Column A and B starting in row 8
Create a 2-d array [(Unit#),(Empty Cell Rent)]. Append ["Total", (SUM formula)] at the end of the array
Write the 2-d array output in Sheet1, starting from Column A row 8
Set background colors for column A and column B
Note:
Since we used Google Sheets event objects, you cannot run this function in your Apps Script editor. Event object is undefined. If you want to debug the code, include logs in the code then modify a cell in your sheet to trigger the onEdit() which contains the event object then check the execution tab for the latest logs
Output:

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 Sheet: Script - Copy/Paste, Time delay, Copy/Paste, repeat for all values in column

I am trying to build a spreadsheet that automatically calculates intrinsic value of a list of stocks.
Cell 3 and cell 4 are linked to another sheet which has a range of formulas including data drawn from external websites.
I would like to write a script for a custom function that will use this setup:
Entered into cell 2, with cell 1 as an argument
Copies the ticker value (cell 1) into cell 3, activating the other sheet functions to do their calculations
After some time (e.g. 30 seconds), reads the value in cell 4 from those other functions
Returns the final value into cell 2.
I would then use this function for all values in column A.
So far Ive been able to figure out how to copy and paste one value. Any guidance would be appreciated, thanks in advance.
function val(ticker) {
var sheet = SpreadsheetApp.getActiveSpreadsheet(); //access to the spreadsheet
SpreadsheetApp.setActiveSheet(sheet.getSheetByName('List of Companies')); //access to the sheet by name
var range = sheet.getRange('A2'); //assign the range you want to copy
var copy = range.getValues();
sheet.getRange('D2').setValue(copy) //new range you want to paste a value
}

google sheets form function not updating with data input from the script

I'm having some trouble getting a function in a google sheet to calculate using values inserted by a script.
I have a google script that is adding data to a sheet based on user-inputted data from a form that the script has created. So, in the form, a user inputs their name, selects a product and some options, and the script adds this to a sheet named 'Client Data Sheet'.
I then have a different sheet which is supposed to do the math to calculate the price. My script copies all the functions from a hidden template row into the next-available row, so, for example, cell D7 contains ='Client Data Sheet'!A4, D8 contains ='Client Data Sheet'!B4 etc... These all display the correct values.
The problem is the price calculation function, in that same calculation sheet, which has a rather complex function that a previous coder wrote. This function should calculate the price of the product and options based on the data in the same sheet. It does so without calling a script, just pulling data from cells in other sheets, running some if() checks to decide whether or not to add extra costs to the total price, and adding it all up.
Problem is, it doesn't update based on the new data. It just shows 0, as though the other cells were empty, even though they now contain data updated by pulling data from another sheet which was edited by a script. If I go in to edit the function and just press enter, it re-calculates correctly, so I basically just need the function to re-evaluate based on the new data that is in the cells it's dependent on.
My theory is that it's not updating the function since I didn't directly edit the cells it's dependent on. I could try to change the awkward, huge function this other coder wrote so it pulls from the spreadsheet my script edits, rather than from cells that copy in that data, but that seems like a workaround, and is unsatisfying.
TL;DR: a function isn't updating based on data that changes in cells that copy the data from other cells which are filled by a script. Anybody have any advice for how to get the function to update?
EDIT: Ok, so if I make sure that the function that isn't updating pulls at least some data from cells that the script updates, it works. It seems that it doesn't recognize that the cell once removed has updated as well. It would be better, though if it was able to pull from the cells that pull their data from the cells the script updates. This would let other users of the sheet change the products if a customer requested a change later on without having to edit my hidden sheets that the data is pulled from.
Code:
This copies a template row in my spreadsheet that contains the math functions:
function new_client(){
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Liquidation');
if(ss.getName()=="Data" || ss.getName().indexOf("Liq")==0){
var last_row = ss.getLastRow();
var last_col = ss.getLastColumn();
ss.insertRowsAfter(last_row, 1);
var template_row = 4;
var copy_range = ss.getRange(template_row, 1, 1, last_col);
var paste_range = ss.getRange(last_row+1, 1, 1, last_col);
copy_range.copyTo(paste_range, {contentsOnly:false});
paste_range.getCell(1,1).setValue("NO");
SpreadsheetApp.flush();
}
}
This copies data from the form into a different spreadsheet, which the sheet edited above pulls data from:
var clientSheet = SpreadsheetApp.getActive().getSheetByName('Client Data Sheet');
clientSheet.insertRowsAfter(clientSheet.getMaxRows(), 1)
var lastRow = clientSheet.getLastRow() + 1;
var lastColumn = clientSheet.getLastColumn();
var destRow = clientSheet.getRange(lastRow, 1, 1, lastColumn);
var column = 1;
for (var key in user) {
if (user.hasOwnProperty(key)) {
destRow.getCell(1, column).setValue(user[key]);
column++;
}
}
So, for example, after this code is run, the Client Data Sheet contains a cell, B4, which now contains the name of the chosen product, "foo". The Liquidation sheet has a cell let's call it A5 that contains the function ='Client Data Sheet'!B4, as well as another cell which has the price calculation function: =if(A5="foo", 100, 0)
When the script above inserts the values from the form, the cell B4 in Client Sheet and the cell A5 in the Liquidation sheet will contain the right value, but the cell with the calculation function =if(B4="foo", 100, 0) will not update.

Google Script to copy specific columns from one sheet to another sheets last row onEdit

I am looking for some help on google app scripting. Currently I have a spreadsheet that has 3 sheets. First sheet is data that comes from a google form. Second sheet is an "open" tab and third is a "closed" tab.
When data comes into the spreadsheet from the form it has only the columns that the form has. The "open" sheet has a combination of data from the form (sheet 1) and some additional cells to add more information to by me and not the person submitting the form. Because of this column miss-match I cannot just copy a whole row from sheet 1 to sheet 2. This also needs to be done onEdit trigger. So when an edit trigger fires, I want to store that rows data into memory and then copy column B to the last row of tab 2, column C. Column C > E, column D > B. etc..
How would I store the whole row into memory (an array I presume), and then copy only specific cells, in a specific order into the last row of a different sheet?
Hope that makes sense :-/
As you said, you'll have to use arrays to get data and write it back to another sheet in your selected order.
This is pretty basic manipulation and there are many ways to achieve it.
One way that is very easy to understand is as follow :
get row data in an array
pick up each value one by one and store in another array
write back to the last row +1 on another sheet.
Note that arrays are indexed starting from 0 , rows and columns start from 1 and A so you'll have to do some math !
function copyRow(){
var ss = SpreadsheetApp.getActive();
var sheet = ss.getActiveSheet();
var rowIdx = sheet.getActiveRange().getRowIndex();
var rowValues = sheet.getRange(rowIdx,1,1,sheet.getLastRow()).getValues();
Logger.log(rowValues);
var destValues = [];
destValues.push(rowValues[0][0]);// copy data from col A to col A
destValues.push(rowValues[0][2]);// copy data from col C to col B
destValues.push(rowValues[0][1]);// copy data from col B to col C
destValues.push(rowValues[0][3]);// copy data from col D to col D
// continue as you want
var dest = ss.getSheets()[1];//go on chosen sheet (here is the 2cond one)
dest.getRange(dest.getLastRow()+1,1,1,destValues.length).setValues([destValues]);//update destination sheet with selected values in the right order, the brackets are there to build the 2D array needed to write to a range
}
If you want that function to run on edit then just call it onEdit() and you're done !
Source: https://stackoverflow.com/a/64297866/14427194
function onEdit(e) {
//e.source.toast("Entry");
//console.log(JSON.stringify(e));
const sh=e.range.getSheet();
if(sh.getName()=="Sheet1" && e.range.columnStart==20 && e.value=="TRUE") {
const tsh=e.source.getSheetByName('Sheet2');
const nr=tsh.getLastRow()+1;
sh.getRange(e.range.rowStart,1,1,12).moveTo(tsh.getRange(nr,1,1,12));
sh.getRange(e.range.rowStart,16,1,4).moveTo(tsh.getRange(nr,16,1,4));
sh.deleteRow(e.range.rowStart);
}
}