Google sheet function into Google script function - google-apps-script

I'm using Google Sheets function for analyze some data, but even if i have not huge database, the sheet is lagging with my function. The function is:
=ARRAY_CONSTRAIN(ARRAYFORMULA(SUMIF(IF(A2:A10000="Received",ROW(A2:A10000),""), "<="&ROW(A2:A10000), B2:B10000)+G1-SUMIF(IF(A2:A10000="Given",ROW(A2:A10000),""), "<="&ROW(A2:A10000), B2:B10000)),COUNTA(B2:B10000),1)
Is it possible to use this function via Google script so as not to overload the sheet?
Example sheet: https://docs.google.com/spreadsheets/d/1UeIXFVsP5hevC20D04juTstBbfViYhWUIp6VRst_Nu4

Try this script. It worked correctly in my copy.
The purpose is to take previous value and add or subtract new value depending on the condition in column A
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = ss.getActiveSheet();
var previous = s.getRange('G1').getValue(); //ger vaule from last month
var valuecount = s.getLastRow() ; // defines number of rows
for (let i = 1; i < valuecount; i++) { //starts loop
var direction = s.getRange(i+1 , 1).getValue();
if (direction == 'Received') {
previous = previous + s.getRange(i+1 , 2).getValue() ;
}
else
{previous = previous - s.getRange(i+1 , 2).getValue() }
s.getRange(2,4,valuecount).getCell(i,1).setValue(previous);
}
}

Related

Properly scripting onEdit

I have a Google Sheets file with 3 spreadsheets. Each of the first 2 contains numerous number data entries. All I want to do is change one cell value on the third (tt) if any data is changed on a different sheet. Looking at my incomplete onEdit() script, you will see I obviously don’t know what I’m doing. How to I change the value of cell ‘a5’ on third spreadsheet (tt) for any changes made anywhere on only the first two?
function onEdit(event)
{
var aa = event.SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Input-Expenses");
var ss = event.SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Input-Income-n-Assets");
var tt = event.SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Run Program");
var r = event.aa.getActiveRange('a1:r105');
var s = event.ss.getActiveRange('d1:q94');
var g = 0
tt.getRange('a5').setValue(g);
}
You need to refer to this article:
https://developers.google.com/apps-script/guides/triggers/
Find Edit trigger.
range A Range object, representing the cell or range of cells that
were edited.
Usage:
var editedRange = event.range;
var editedSheet = editedRange.getSheet();
if (editedSheet.getName() === 'Sheet1')
{
var targetSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet2");
// do smth
}
This should do what you want
function onEdit(event) {
try {
var aName = event.range.getSheet().getName(); // Active sheet
if( ( aName === "Input-Expenses" ) || ( aName === "Input-Income-n-Assets" ) ) {
// You said anywhere on these 2 sheets and put in Run Program:A5
event.source.getSheetByName("Run Program").getRange("A5").setValue(0);
}
}
catch(err) {
SpreadsheetApp.getUi().alert(err);
}
}

How can I check one value against another value in my Google Sheet and then query properties of the cell with the matching value?

I want to check values from a JSON URL against values within a defined range in my Google Sheet.
Should there be a match, I would like to query the properties of the cell containing the matching value (its row, its column, etc.)
How can I do this within Google Apps Script?
Everything you need to know about Spreadsheet operations in Apps Script can be found in Overview and SpreadsheetApp. You can start from there. For example, the methods to query properties like row and col are getRow and getColumn
Never mind. Got to my answer myself.
var i = 0;
// Bringing in the data from the third-party into Google Sheets.
var groupJSON = UrlFetchApp.fetch('<ANY-JSON-URL>');
var groupObjectRaw = JSON.parse(groupJSON);
// var groupObject = groupObjectRaw[0]; <--optional, for my use only
var membersGroupForm = groupObject.data['member'];
var projectsGroupForm = groupObject.data['project'];
var hoursGroupForm = groupObject.data['hours worked'];
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Details');
var membersRange = ss.getRange('Details!A:A');
var membersSheet = membersRange.getValues();
var projectsRange = ss.getRange('Details!1:1');
var projectsSheet = projectsRange.getValues();
function getRowNumber() {
for (i; i < membersSheet.length; i++) {
if (membersSheet[i][0] == membersGroupForm) {
return i + 1;
}
}
}
var rowNumber = getRowNumber(i, membersSheet, membersGroupForm);
Logger.log(rowNumber);
function getColumnNumber() {
for (var row in projectsSheet) {
for (var col in projectsSheet[row]) {
if (projectsSheet[row][col] == projectsGroupForm) {
return parseInt(col) + 1;
}
}
}
}
var columnNumber = getColumnNumber(projectsSheet, projectsGroupForm);
Logger.log(columnNumber);
var cell = ss.getRange(rowNumber, columnNumber);
cell.setValue(hoursGroupForm);

Google sheets, make active range the top left cell of the window

Essentially, I have a few sheets with the following setup:
A few frozen title rows at the top of the sheet, underneath that many rows of data all with a date in column A. The following function allows me to jump to 'today' on any sheet (well, technically the closest day in the future if there isn't data for today).
function goToToday() {
var workbook = SpreadsheetApp.getActiveSpreadsheet()
var currentSheet = workbook.getActiveSheet().getName();
if (currentSheet !== "Calculations") {
workbook.getSheetByName(currentSheet).setActiveRange(workbook.getSheetByName(currentSheet).getRange(1,1))
var today = new Date()
var rowNumber = 4
var numOfRows = workbook.getSheetByName(currentSheet).getLastRow() - 3
var dates = workbook.getSheetByName(currentSheet).getRange(4,1,numOfRows,1).getValues()
for(i = 0; i < numOfRows; i++) {
if(dates[i][0] > today) {
break
}
else {
rowNumber++
}
}
workbook.getSheetByName(currentSheet).getRange(rowNumber, 1).activate()
}
}
What's bugging me is if active cell prior to running the function is below the returned 'today' cell of the function, the cell is returned as the top left cell in the window, which is perfect. If however the current active cell is above the returned cell, the cell is returned near the bottom of the window. How can I make the function return the cell consistently as the top left cell of the window? I assume this has something to do with scrolling..
function goToToday() {
var workbook = SpreadsheetApp.getActiveSpreadsheet(),
currentSheet = workbook.getActiveSheet();
if ( currentSheet.getName() !== "Calculations" ) {
var lastCell = currentSheet.getRange(
currentSheet.getMaxRows(),getMaxColumns()
);
currentSheet.setActiveRange("A1");
var today = new Date(),
rowNumber = 4,
numOfRows = currentSheet.getLastRow() - 3,
dates = currentSheet.getRange(4,1,numOfRows,1).getValues();
for(i = 0; i < numOfRows; i++) {
if(dates[i][0] > today) {
break
}
else {
rowNumber++
}
}
currentSheet.setActiveRange(lastcell);
SpreadsheetApp.flush();
currentSheet.getRange(rowNumber, 1).activate()
}
}
I refactored some function calls, but the main idea is to set the active range to be the bottom right cell, flush the sheet (which should update the UI) and then set the active range to the target.
EDIT: In refactoring the OP's code, I moved the call to .getName() into the conditional, and just stored the sheet in currentSheet. This eliminated multiple calls to worksheet.getSheetByName(currentSheet). Not a really big deal here, but in a larger script, run time would suffer. And, IMO, it's easier to read.
I don't think they are methods in Google Apps Script that can control the scroll, you can request a new feature.
A workaround (not the best one) is to use hideRows() and inmediately call showRows() that will make the UI "scroll". An example assuming the first row is frozen:
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
sheet.hideRows(2, 199);
sheet.showRows(2, 199);
sheet.getRange(200, 2).activate();
}
Adapting HardScale's solution, I got this to work. I'ts not the most well written and I'm sure there are plenty of ways to 'thin in out' so to speak, but it works!
function goToToday() {
var workbook = SpreadsheetApp.getActiveSpreadsheet()
var currentSheet = workbook.getActiveSheet().getName();
if (currentSheet !== "Calculations") {
var lastCell = workbook.getSheetByName(currentSheet).getRange(workbook.getSheetByName(currentSheet).getMaxRows(),workbook.getSheetByName(currentSheet).getMaxColumns());
workbook.getSheetByName(currentSheet).setActiveRange(workbook.getSheetByName(currentSheet).getRange(1,1))
var today = new Date()
var rowNumber = 4
var numOfRows = workbook.getSheetByName(currentSheet).getLastRow() - 3
var dates = workbook.getSheetByName(currentSheet).getRange(4,1,numOfRows,1).getValues()
for(i = 0; i < numOfRows; i++) {
if(dates[i][0] > today) {
break
}
else {
rowNumber++
}
}
workbook.getSheetByName(currentSheet).setActiveRange(lastCell);
SpreadsheetApp.flush();
workbook.getSheetByName(currentSheet).getRange(rowNumber, 1).activate()
}
}

Google App Script - loops not executing

I want to create a function that will iterate over every sheet until a given sheet. The function receives the name of that last sheet as an argument.
function getUntilMonthSavings(month) {
var spreadsheet = SpreadsheetApp.getActive();
var monthSheet = spreadsheet.getSheetByName(month);
var allSheets = spreadsheet.getSheets();
var sheetNumber = monthSheet.getIndex();
var totalSavings=0;
for (var i = 1; i < monthSheet; i++){
totalSavings = totalSavings + allSheets[i].getRange("I20").getValue();
}
return totalSavings;
}
My problem is that what is returned is always 0. I've also returned i to check if it is being iterated, but it returns 1 even when the sheet index is greater than 1.
I'm sure to be doing some kind of basic blunder, but I'm quite at a loss as why this code is not working.
MonthSheet is an object and not something you can compare i to in your loop. You need the actual index of the sheet referred to.
Try:
for (var i = 0; i < monthSheet.getIndex(); i += 1) {

How to replace text in Google Spreadsheet using App Scripts?

I tried to get the value of a range and than remove all points from the cell.
var FILE = SpreadsheetApp.openById("xyz");
var CONTENT = FILE.getSheetByName("Sheet1");
var A1 = CONTENT.getRange("I17").getValue();
A1. replace(".", "");
It gives me that can't find the replace function. Is there any function in Google Apps Script that allows me to replace the string?
If this is an exact copy of your script then you have a space in-between A1. and replace but I assume it is not.
#SergeInsas is right the content needs to be a string for the replace() function to work, so if your trying to replace the . in a decimal number then you can use the toString() method first like below.
var FILE = SpreadsheetApp.openById("xyz");
var CONTENT = FILE.getSheetByName("Sheet1");
var A1 = CONTENT.getRange("I17").getValue();
var A1String = A1.toString().replace(".", "");
Or you can multiply the number to get rid of the decimal but this will depend on how many decimal places you have :)
There is a more powerful, and simpler, method available: TextFinder.
The accepted answer to this question requires an additional step to post the replaced string back to the cell.
The TextFinder method does not need you to write the data back to the cell.
And if you want to search multiple cells, then this method saves you the iterations.
var FILE = SpreadsheetApp.openById("xyz");
var CONTENT = FILE.getSheetByName("Sheet1");
var A1 = CONTENT.getRange("I17");
A1.createTextFinder(".").replaceAllWith("");
I haven't tested it on a large data set but I suspect this would be quite quick.
Edit: I wrote a short tutorial on this.
For some reason, this solution doesn't work for me. Here is my whole code that should replace the '+' symbol with 'nothing'
// I need to replace more occurrences of different strings, so this is just an example..
var ui = SpreadsheetApp.getUi();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var range = ss.getRange("G5:G7").getValues();
// this is a loop, to go through multiple cells that may contain the text, that needs to be replaced.
for (var i = 0 ; i<range.length ; i++) {
var le = range.length;
var stri = range[i].toString().replace("+", "");
Logger.log(stri);
}
var msg = ui.alert("Replaced?");
return msg;
Hope this help you
function removeAccents() {
var spreadsheet = SpreadsheetApp.getActive();
var range = spreadsheet.getRange("F3:F");
var data = range.getValues();
for (var row = 0; row < data.length; row++) {
for (var col = 0; col < data[row].length; col++) {
data[row][col] = (data[row][col]).toString().replace(/é/g, 'e');
data[row][col] = (data[row][col]).toString().replace(/ã/g, 'a');
}
}
range.setValues(data);
};
Sharing a very helpful solution from Bannager Bong on this Google Docs Editor Help Forum thread. Made a slight modification to the function so that it accepts arguments for the find, replace values and then added a range argument so that the function can target a specific region. Even so, this method is extremely slow (my sheets have 5k rows).
function Cleanup12m() {
var spreadsheet = SpreadsheetApp.getActive();
//fandr(",", "");
//fandr("\"","");
fandr("�","",spreadsheet.getRange('BA:BA')); //uses specific range
};
function fandr(find, repl) {
var r=SpreadsheetApp.getActiveSheet().getDataRange();
var rws=r.getNumRows();
var cls=r.getNumColumns();
var i,j,a,find,repl;
//find="abc";
//repl="xyz";
for (i=1;i<=rws;i++) {
for (j=1;j<=cls;j++) {
a=r.getCell(i, j).getValue();
if (r.getCell(i,j).getFormula()) {continue;}
//if (a==find) { r.getCell(i, j).setValue(repl);}
try {
a=a.replace(find,repl);
r.getCell(i, j).setValue(a);
}
catch (err) {continue;}
}
}
};
//Revised to apply to a selected range
function fandr(find, repl, range) {
var r= range;//SpreadsheetApp.getActiveSheet().getDataRange();
var rws=r.getNumRows();
var cls=r.getNumColumns();
var i,j,a,find,repl;
//find="abc";
//repl="xyz";
for (i=1;i<=rws;i++) {
for (j=1;j<=cls;j++) {
a=r.getCell(i, j).getValue();
if (r.getCell(i,j).getFormula()) {continue;}
//if (a==find) { r.getCell(i, j).setValue(repl);}
try {
a=a.replace(find,repl);
r.getCell(i, j).setValue(a);
}
catch (err) {continue;}
}
}
};