Adding a Range to existing data in the row below - Apps Script - google-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.

Related

Automate copying a value from one cell to another on a specific date

I'm looking for more of a pointer to some documentation of a method here rather than an exact solution, I'm happy with JavaScript, haven't done much Apps Script and I'm moderate to OK familiar with Google Sheets functions but far from expert.
I have part of a Google Sheet with some date specific data on it like this:
Date
Some-Value
1 Jan 2023
123
15 Jan 2023
456
... etc
In another part of a sheet I have a cell with the current value of Some-Value. This cell in fact contains a formula that totals a column on another sheet for the values. In case it's not blindingly obvious, these dates are in fact sprint end dates and the Some-Value is a count of story points extracted from JIRA. So sort of like this:
Current Value of Some-Value
345
On exactly the date in the Date column I want to copy the value from the "Current Value of Some Value" cell into the cell in the "Some Value" column adjacent to that date. I don't care if that copy happens only once on that day, or several times on that day, or every hour on that day, or whatever. I just want it to happen at least once, automatically.
Should I be looking at an Apps Script function to do this, and roughly how should I do that? Or is there a simpler way of using some Google Sheets function to copy that cell?
You could potentially do this with formulas if you are willing to enable iterative calculation for your sheet. In that case, you could then write something like =if(A2=today(),currentValueCell,B2) (I'm assuming your Date/Some-value table is in A1:Bx of a sheet and you are placing the above in B2). This will return the current value only if the date matches, and then when the dates no longer match will just maintain whatever value is already present in the cell.
EDIT
Ah yes, I forgot that the initial state of a self-referencing IF is zero (rather than null) until a TRUE occurs. Try =if(A2=today(),currentValueCell,if(B2<>0,B2,)) to hide the initial zero generated when the date in A2 is not equal to TODAY().
If you need to copy a value than then it's going to be changed or erased, yes, you'll need an AppScript. If you already know something, you'll more than able to set a simple function to do it.
You can look into Installable Triggers that will help you to set when and how frequently you want your script to be fired; and it will notify you if there are errors in any of that executions.
Sometimes, matching dates can be tricky, more if you have them previously written. If you have to do a timestamp, then you'll probably succeed at first. Just for have it handy: https://developers.google.com/google-ads/scripts/docs/features/dates
Remember to always use Logger.log() to track the progress of your code and what it's returning. Good luck and here you can always ask for specific struggles you may find!
(I'm no expert at programming by far, since it's copying only a value, you may find useful to record macros and watch them to learn how to copy in the formats you need - only values, paste format, etc. - if you don't copy, you can also research in .getValue() and .setValue() )
In case anyone else comes across this in future, here is the somewhat creaky Apps Script function I developed to do this. It's my first Google Apps Script function so feel free to tear it to shreds.
I then added a trigger to run this function weekly, which was easier than I had thought -- a bit of google searching for Apps Script time based triggers found me the answer.
Nonetheless thanks to the people who answered earlier because the clues you gave me helped me find the answer in the end.
function updateUnderTest() {
var activeSheet = SpreadsheetApp.getActiveSpreadsheet();
var sourceSheet = activeSheet.getSheetByName('Supporting Data');
// I12 is the cell that gets updated daily with the story point count
var value = sourceSheet.getRange('I12').getValue();
// D3:E27 is the range containing date : value pairs
var data = sourceSheet.getRange("D3:E27").getValues();
// epochNow will be the current epoch time, in milliseconds
var epochNow = new Date().valueOf();
// Look through the data range to find a matching date
for (nn=0;nn<data.length;++nn) {
// If a match is found to the nearest day, break the loop
if ((data[nn][0].valueOf() < epochNow) && ((epochNow - data[nn][0].valueOf()) < (24*3600*1000) )) {break};
}
// only update if we found a match
if (nn < 24) {
sourceSheet.getRange(nn+3, 5).setValue(value);
}
}

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.
}

How do I keep values in 2 different sheets in sync?

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

Why does my Apps Script reference the wrong row?

The Goal
I am trying to create a spreadsheet using some custom functions. The purpose for the sheet is to keep score in a quizzing competition. The top row has the question number, the second row the score, and the third number the number of fouls for that question.
The Problem
After noticing some problems with the score calculation, I was able to narrow the problem down to the part of the code where I add up the fouls that occurred prior to the current question. It seems that no matter what I do, the code sums over the question row, not the foul row.
Attempted Solutions
The extremely strange thing is that no matter what I change the reference cells to, it always produces the same result (i.e. it still references the question row same as it always has).
Example
I'm not sure if that makes any sense, but I've made an example sheet here so you can see what I'm talking about and try to figure out what is going on. Keep in mind that I'm well aware that I could accomplish what I'm trying to do in the example sheet with a simple built-in formula, but since there's no way to use worksheet formulas on the Apps Script side, I have to create my own version of it. I've made the example editable for anyone with the link, so you should have full access to it; but if you have problems, just let me know, and I'll see what I can do to fix it.
In your For loop, you are summing the indexes rather than the values:
Try:
for (var PrevValue in PrevValues[0]) {
Sum = Sum + Number(PrevValues[0][PrevValue]);
}
EDIT:
You'll also need to account for the case where you pass in a single cell rather than a range (=mySum($B4:B4)), because in that case the value is passed directly instead of an array.
if(PrevValues instanceof Array){
for (var PrevValue in PrevValues[0]) {
Sum = Sum + Number(PrevValues[0][PrevValue]);
}
}else
Sum = PrevValues;