How do I keep values in 2 different sheets in sync? - google-apps-script

I have a sheet that has some dates on it, like "25/02/2016", listed down a column. On the cells to the right of each date, there are some numeric values.
I need to copy these numeric values to a specific range on a different sheet. Ideally, it would work like a one-way sync, where I would write values on sheet1 and sheet2 would automatically be updated.
I've been looking through the Google Apps Script documentation, but I have no idea where to start. I do have some pseudo-code, just don't how to use it here.
function getDates() {
for (count = 0; count < sheet1.length; count++) {
if (hasDate) {
return(cell);
}
}
}
var numericValuesRows = sheet1.getDates().getRow();
var numericValuesRange = numericValuesRows.selectColumns(C-F);
Just making up method names and syntax. This bit is supposed to find out which rows have dates in them, and then select columns C to F in those rows. The next one is supposed to select the destination as all the cells from row3:columnC to row10:columnF, and copy the previously selected values to there.
var outputRange = sheet2.cellRange(C3-F10);
numericValuesRange.copyTo(outputRange);
I realise it's really crappy pseudo-code, but I'm hoping it at least helps in some way get across what I want to do. What would be the best way to do this?

Use the onEdit() trigger which will trigger when you edit a sheet. You can check the source of the edit to make sure its an edit to the cells you want in a particular sheet. Once that's done, its a matter of using getValue(), setValue() and getSheetByName(). Start here: simple triggers

Related

Adding a Range to existing data in the row below - Apps Script

I'm very new to coding and specifically to JavaScript.
Im trying to use Apps Script to code my google sheet to automate a financial spreadhseet i am making, here is my problem:
I want to input data in a single row, and have it update the row below, for example i want the range B7:M7 to be my input, so lets say for the sake of simplicity all 12 cells in that row will have the value 50
I then want it to transfer onto the range B8:M8, but if that range is already on 50, then i want it to minus that and make it 0. If range B8:M8 was on 0, then i want to make it -50. I hope this makes sense. Thank you
As doubleunary pointed out in the comments, your question lack info about the case, but if you use this code you can get the result you need exactly as you asked for.
please mind that here the range is fixed to work on B7:M7 to B8:M8 as you said.
You can change the getRange() numbers to work with the range you need, even with a dynamic range.
function test(){
var value1 = SpreadsheetApp.getActiveSheet().getRange(7,2,1,12).getValues();
var value2 = SpreadsheetApp.getActiveSheet().getRange(8,2,1,12).getValues();
for(var i=0;i<12;i++)
{
value2[0][i] = value2[0][i]-value1[0][i];
}
SpreadsheetApp.getActiveSheet().getRange(8,2,1,12).setValues(value2);
}
Please, update your question as the community guidelines need.

Using named ranged in google sheets as variables in custom formulas

Not sure if this is going to make sense but here I go.
What I want to do is to create a formula that isn't linked to a cell directly. In example: if I want to calculate carryweight for a tabletop game like D&D I would need the formula (strengthBonus x 5). For my current attempt I renamed the range (cell rather) strengthBonus to MOD_STR so when I put the formula =(multiply(MOD_STR,5) it works like a charm. Then I named that range "CARRYWEIGHT" and then use it elsewhere.
What I would like to be able to do is to make a new variable, similar to the way that "Define Named Range" does, but instead of relying on the variables being somewhere on the spreadsheet they would process from an internal formula. For example, if I type =carryweight into a cell it would run the equation =MULTIPLY(MOD_STR,5) in that cell and output the answer. I know nothing about code yet but have just been pointed in the direction of tutorials but I'm also asking for help here.
The code I have tried is
function CARRYWEIGHT(MOD_STR){
return MOD_STR*2}
and something else, I can't remember what but I got it to at least accept it in the spreadsheet. When I type it in I get an error stating that the outcome isn't a number.
I have no idea where to go from here.
Thank you in advanced for your help.
The difference between sheets formulas and Apps Script is that in Apps Script you need to retrieve the value of the range corresponding to the name of a named range
You cannot simply multiply the name of the range (which is a string!) with a number
Here is a sample of how to retrieve a range by name and make calculations wiht the value stored in it:
function CARRYWEIGHT(MOD_STR){
// retrieve all named ranges in the spreadsheet
var namedRanges = SpreadsheetApp.getActive().getNamedRanges();
//loop through all the results
for (var i = 0; i < namedRanges.length; i++){
var range = namedRanges[i];
//if the range with the name equal to the value of MOD_STR is found, get the cell content of this range
if(range.getName()==MOD_STR){
var value = range.getRange().getValue();
// perform the calculation with the cell content of the named range
return value*2;
}
}
}
From the cell, call the function as =CARRYWEIGHT("paste here the name of the range of interest"), do not forget the quotes (unless it is a cell reference)!
I hope this helped you to get started, for further understanding plese consult the following references.
References
Named Ranges
Loops
Conditional statements
Ranges
getValue()

How do I conditionally clear content in a row contained in a range based on a checkbox?

I am working on a sheet that I need to be able to clear a row of data at a time without breaking the formatting.
What I want it to do:
If checkbox in column "J" is checked and they click the reset button, it clears all rows with checked "J" and then sorts the remaining data by column 'C'.
What I have done:
I have cobbled together most of it, but when I run it, it clears the whole range rather than just the rows with the checked box.
This is the script I have so far, and I feel I'm close but can't see where my error is:
function ResetCompletedOnly() {
var sheet = SpreadsheetApp.getActive().getSheetByName('WITH RESET');
var r = sheet.getRange('B15:P51');
var v = r.getValues();
for(var i=v.length-1;i=1;i--)
if(v[9,i]=1){
r.clearContent(i+1)}
else if(v[9,i]=0){
r.sort(3)
}
};
9/24/19 ADDED THE
EXAMPLE SHEET HERE(link)
Ideally what I'm trying to do and why:
BEFORE HITTING RESET(img) the sheet is a jumble of completed sales which have been installed or otherwise activated but the sales person hasn't been paid out for, orders that have a delayed install date, or are delayed due to a dispute which has to be resolved via inquiry. Ideally once the sale has been paid out, there is no further action required by the sales rep, so it's ok to clear this data.
CLEARING rather than DELETING is crucial due to the formatting and the constant daily reuse/refresh of the sales tracker.
The folks I'm making this tracker for are very computer challenged (Copy Paste is a difficult concept for them) and so it needs to be simple, easy, and clean for them to use continuously.
AFTER HITTING RESET(img) only the rows which were paid out and required no further action from the sales person have been cleared. Afterwards, the remaining rows were then sorted according the date they were first entered into the sheet so that the oldest and most pressing is always at the top.
Sorry for the delay in the update, it's been a crazy week and I wanted to make sure I tinkered with all the suggestions first before posting the update. I'm still digging into the tutorials and information given but am struggling a little to fully grasp the scripting language and what I'm trying to ask the script to do.
THANK YOU EVERYONE FOR YOUR SUGGESTIONS SO FAR!
Try something like this:
When you use range.getValues() you get a two dimensional array [][] or an array of arrays. In this case vA[0][0] is the value in B15 and vA[1][0] is the value in B16. So the first index is for the rows and the second is for the columns although the arrays start with zero and row and columns start with one.
There is not a range.clearContents(p) function so I assumed you wanted to delete that row. vA[0] is actually row 15 so that's why the 15 in sh.deleteRow(i+15-d++); and the d just keeps track of how many rows have been deleted. I didn't see much sense in sorting during the for loop because that would potentially mess all the rows up so I waited until after the for loop was completed. This may not be what your after...we can discuss that further.
function ResetCompletedOnly() {
var sh=SpreadsheetApp.getActive().getSheetByName('WITH RESET');
var rg=sh.getRange('B15:P51');
var vA=rg.getValues();
var d=0;
for(var i=0;i<vA.length;i++) {
if(vA[i][8]==1){//column J
sh.deleteRow(i+15-d++);
}
}
rg.sort({column:3,ascending:true});//column C
}
References
Sheet.clearContents
Range.getValues()
Sheet.deleteRow()
Range.sort()
This is probably not the exact answer that your looking for but hopefully it will help you to learn a little more about Google Apps Script in order to get the results that you need.
You did an excellent job in creating your question although a bit more research in to the Google Apps Script documentation would have been helpful.
I can't really test this without your example spreadsheet but I think this will clear all of the row cells that don't have formulas. It will run much slower because I'm clearing them one cell at a time so that I can avoid messing with the formulas. I tested it a little with random numbers and it works.
function ResetCompletedOnly() {
var sh=SpreadsheetApp.getActive().getSheetByName('WITH RESET');
var rg=sh.getRange('B15:P51');
var vA=rg.getValues();
var vF=rg.getFormulas();
for(var i=0;i<vA.length;i++) {
if(vA[i][8]==1){//column J
for(var j=0;j<vA[i].length;j++) {
if(!vF[i][j]) {
sh.getRange(i+15,j+2).setValue('');
}
}
}
}
rg.sort({column:3,ascending:true});//column C the column number is not relative to the range.
}

Trigger Email if values in column change by x % (sending range of data)

I'm new to Google Scripting and I need some help as I've been searching and tinkering to no avail. What I have is a google sheet with 12 columns of data and a growing number of rows. What I'd like to do is have an email sent to me if a value in one of those columns (column K) changes by more than 10% from it's previous value. Furthermore, I'd like the email to contain the change in it with data from columns A:D (ie. This product has been changed by more than 10%), including a link to the sheet in question (will always be the same sheet).
I'm not sure if it's possible to have this log and perform an email update every hour (or custom) as opposed to right away. If no changes, the email would say "no changes".
I know I'm asking for a lot but I'd really like some help, even if you tell me other examples that get me close to what I'm asking for. Thanks in advance. I really appreciate it.
White Google Sheets allows you to view the revision history for a Sheet, I do not see an API function to retrieve a past revision. Therefore, I think you will need to design a solution that saves the values that you wish to compare against. You could save these values to another Google Sheet, or you could save each value into a named script property. The ultimate size of your spreadsheet would determine the best approach.
You will need to create a script function and cause it to be run from a timed trigger once per hour. That script will grab a range of values
//Grab 10 rows from column K
var vals = SpreadsheetApp.getActiveSheet().getRange(1, 11, 10, 1);
To save these values (for the next execution). In this example, the row number is being used as the key to the property object. You might wish to use a value from another column instead.
var docProp = PropertiesService.getDocumentProperties();
for(var i=0; i<vals.length; i++) {
docProp.setProperty(i, vals[i][0]);
}
To perform your comparison of new values to prior values, you will just need to compare the values in docProp before saving the value.
var docProp = PropertiesService.getDocumentProperties();
for(var i=0; i<vals.length; i++) {
var old = docProp.getProperty(i);
//perform your comparison here. Take action if value changes by more than 10%
Logger.log("Old "+old+"; New "+vals[i][0]);
docProp.setProperty(i, vals[i][0]);
}

copy spreadsheet rows from one sheet to another conditional on name of sheet

Please help this non-expert -- his job depends upon it.
I have a Google Form that feeds answers into a sheet on a Google Spreadsheet -- let's call it the "main sheet." One of the form's questions asks for a job number. I have been able to figure out how to take each unique job number respondents have entered and create a sheet by that name in the same spreadsheet. I even have copied the headers for the form's answers at the top of each of the new sheets.
What I cannot get to work is rifling through all of the answers in the main sheet, sans the column headers, and copy the rows to the sheet names based on a conditional match with the job number value equaling the sheet name value. So if someone applies for job number 65, that response gets copied from the main sheet to sheet with the name "65."
For starters, I may have the loops set up incorrectly, trying to exclude the header in the main sheet and creating the array of all of the sheet names.
But a second problem I have is that I need to use the variables for both the sheet name value and the job number value. I need to be able to account for an ever increasing job numbers. Staff do not want to have to create a new sheet with every new job number -- they want that done automatically when a user fills in a new job number.
I am happy to share my work, as it is, with anyone who can help point me in the right direction.
+++
Solved, so I yanked down the link to the spreadsheet. In spite of myself, it appears, this community was able to help me out. Thanks.
This function that I wrote will help you find your job number:
function searchColumn(value, rangeValues) {
for (var i = 1; i < rangeValues.length; ++ i) {
if (rangeValues[i] == value) return i;
}
return -1;
}
Give it an array of values (usually by Range.getValues()), and the value you want to search and it will return the row number where that value was found. If it didn't find it, it returns -1.
example:
// Gets all data in the first column of the sheet
var valuestoSearch = mySheet.getRange(1, 1, sheet.getLastRow()).getValues();
// then
var rowNum = searchColumn(job_number, valuesToSearch);
Now that you have the row number, you can use:
var rowData = mySheet.getRange(rowNum, 1, 1, mySheet.getLastColumn()).getValues();
This will give you all the values in that row up to the last column in the sheet.
Once you have that data, you can copy it to another sheet by using:
var jobSheet = mySpreadsheet.getSheetByName(job_number);
jobSheet.getRange(jobSheet.getLastRow() + 1, 1, 1, rowData.length).setValues(rowData);
Or something very similar to this. Then you may want to erase the data in the old spreadsheet so that you don't keep repeating the same operation. But that's up to you how you want to handle that. Take a good look at the documentation for SpreadsheetApp, as it has everything you're looking for and more :)