Google App Script Get Multiple Index Positions - javascript

I'm trying to figure out a way to get the index position of each repetition of a string.
The goal is to get each of those index positions and use the difference between each position to create a subtotal. The problem at hand is two fold. First, the distance between each string is not a standard length. Second, I seem to not be able to find out how to get multiple index positions of a particular string. I'm currently using the following code to cycle through different ranges to insert equations in to the sheet.
var Architecture = function(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var activeSheet = ss.getActiveSheet();
var data = activeSheet.getRange(18, 2, 7, activeSheet.getLastColumn()).getValues();
for (var i = 0; i < data.length; i++){
var rowData = data[i];
var checkData = data;
var row = checkData[i];
var colB = row[0];
if(colB == 'Subtotal'){
activeSheet.getRange(18 + i, 5, 1, data[0].length-4).setFormula('=iferror(sum(E' + (i+12) + ':E' + (i+17) + '))');
}else{
activeSheet.getRange(18 + i, 5).setFormula('=iferror(sum(F' + (i+18) + ':DU' + (i+18) + '))');
activeSheet.getRange(18 + i, 6)
.setFormula('=iferror(sum(filter(Invoices!$E:$E,Year(Invoices!$B:$B)=year(F$12),MONTH(Invoices!$B:$B)=Month(F$12),Invoices!$F:$F=$B' + (i+18) + ',Invoices!$A:$A=$C$2)))')
.copyTo(activeSheet.getRange('F18:DU23'));
}
}
};
var DueDiligence = function(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var activeSheet = ss.getActiveSheet();
var data = activeSheet.getRange(26, 2, 15, activeSheet.getLastColumn()).getValues();
for (var i = 0; i < data.length; i++){
var rowData = data[i];
var checkData = data;
var row = checkData[i];
var colB = row[0];
if(colB == 'Subtotal'){
activeSheet.getRange(26 + i, 5, 1, data[0].length-4).setFormula('=iferror(sum(E' + (i+12) + ':E' + (i+25) + '))');
}else{
activeSheet.getRange(26 + i, 5).setFormula('=iferror(sum(F' + (i+26) + ':DU' + (i+26) + '))');
activeSheet.getRange(26 + i, 6)
.setFormula('=iferror(sum(filter(Invoices!$E:$E,Year(Invoices!$B:$B)=year(F$12),MONTH(Invoices!$B:$B)=Month(F$12),Invoices!$F:$F=$B' + (i+26) + ',Invoices!$A:$A=$C$2)))')
.copyTo(activeSheet.getRange('F26:DU39'));
}
}
};
The goal would be to combine all these different functions into a single array that runs much quicker than the 18 seconds it currently takes to run.
EDIT
I've made some progress. Using the below function, I can get the index of each string, but it repeats that index position in the log for the number of rows that preceded it. For example, if the string is in index position two and then index position 10, the log shows 2,2,2,10,10,10,10,10,10,10,10 (only eight positions because 10-2=8).
var test = function(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var activeSheet = ss.getActiveSheet();
var data = activeSheet.getRange(14, 2, activeSheet.getLastRow()-13,1).getValues();
var newData = data.map(function(r){ return r[0]; });
//Logger.log(newData);
//Logger.log(data.indexOf("Subtotal", [i]));
for(var i = 0; i < data.length; i++){
Logger.log(newData.indexOf("Subtotal", [i]));
// }
}
};

You want to retrieve the indexes of all elements of an array called "Subtotal".
This can be done in a single line by combining map and filter:
var indexes = newData.map((element, i) => element === "Subtotal" ? i : "")
.filter(element => element !== "");
In this case, map returns an array with the matching indexes as well as empty strings (for unmatching indexes), and filter removes the empty string elements.
Edit:
If you want to retrieve an array with the differences between an index and the previous one, you can do this (the first index will be the same in this case):
var differences = indexes.map((element, i) => i == 0 ? element : element - indexes[i-1]);
Reference:
Array.prototype.map()
Array.prototype.filter()

Here's an example:
function getRowNumbersOfSameStrings() {
const ss=SpreadsheetApp.getActive();
const sh=ss.getActiveSheet();
const rg=sh.getRange(2,1,sh.getLastRow()-1,1);
const vs=rg.getValues();
let u={};
vs.forEach(function(r,i){
if(!u.hasOwnProperty(r[0])) {
u[r[0]]=[];
u[r[0]].push(i+2);//row numbers
}else{
u[r[0]].push(i+2);//row numbers
}
});
SpreadsheetApp.getUi().showModelessDialog(HtmlService.createHtmlOutput(JSON.stringify(u)), "Results");
}
Here's my sample data:
COL1,COL2,COL3,COL4,COL5,COL6,COL7,COL8
7,2,4,0,1,1,10,4
9,3,2,5,2,6,7,6
9,2,10,10,9,7,2,11
10,7,2,10,2,6,2,0
0,2,4,0,6,3,10,0
2,7,6,7,0,3,5,8
6,9,6,11,3,5,5,8
1,4,5,5,10,0,3,11
4,3,3,3,1,9,3,2
9,4,0,9,2,8,11,2
5,8,7,0,3,6,5,4
5,3,5,3,2,3,8,3
Results:
{"0":[6],"1":[9],"2":[7],"4":[10],"5":[12,13],"6":[8],"7":[2],"9":[3,4,11],"10":[5]}

Related

If cell is empty, make reference to another cell of other column in the same row

I would like to do this in G column : If Cell 'G8' is Blank Then (=K8) for example. My difficulty is to make reference of same row in K of empty cell in G.
I've tryed to adapt this script but I get a shift at some point and I don't know why.
function updatewithformula() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Sheet 1');
var range = sheet.getDataRange()
var source = sheet.getRange('G1:G13').getDisplayValues()
var index = []
for (var i = 1; i<source.length; i++){
if (source[i][0] == ""){
index.push(i+1)
}
}
index.push(range.getLastRow()+1)
Logger.log(index)
for(var i = 0;i<index.length-1;i++){
var rangetomodify = sheet.getRange(index[i],7,1,1)
var l = index[i+1]-index[i]-1
rangetomodify.setFormulaR1C1("=R["+l+"]C[4]")
}
}
As another approach, how about the following modification?
Modification points:
About I've tryed to adapt this script but I get a shift at some point and I don't know why., in your script, "=R[" + l + "]C[4]" is used with var l = index[i + 1] - index[i] - 1. In this case, when l is not 0, the other row is used. I thought that this might be the reason for your issue. In your script, I think that it is not required to use var l = index[i + 1] - index[i] - 1. When your script is simply modified, it becomes as follows.
function updatewithformula() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Sheet 1');
var range = sheet.getDataRange();
var source = sheet.getRange('G1:G13').getDisplayValues();
var index = [];
for (var i = 1; i < source.length; i++) {
if (source[i][0] == "") {
index.push(i + 1);
}
}
index.push(range.getLastRow() + 1);
for (var i = 0; i < index.length - 1; i++) {
var rangetomodify = sheet.getRange(index[i], 7, 1, 1);
rangetomodify.setFormulaR1C1("=R[0]C[4]");
}
}
But, in this case, getRange and setFormulaR1C1 are used in a loop. In this case, the process cost becomes high.
When your script is modified by reducing the process cost, how about the following modification?
Modified script:
function updatewithformula() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Sheet 1');
var source = sheet.getRange('G2:G13').getDisplayValues();
var ranges = source.reduce((ar, [g], i) => {
if (!g) ar.push(`G${i + 2}`);
return ar;
}, []);
sheet.getRangeList(ranges).setFormulaR1C1("=R[0]C[4]");
}
By RangeList, the process cost can be reduced a little.
References:
reduce()
getRangeList(a1Notations)
setFormulaR1C1(formula) of Class RangeList
There are better, easy and short methods to do it, I have used offset here.
The below works for me
function updatewithformula(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Sheet 1');
var range = sheet.getDataRange()
var source = sheet.getRange('G1:G13').getValues();
Logger.log(source);
var index = [];
for (var i in source){
if(source[i][0] == ""){
var j = +i;
index.push(("G"+(j+1)));
}
}
for (var i in index){
var TEMP = sheet.getRange(index[i]);
var TEMP1 = TEMP.offset(0,4).getA1Notation();
const formula = `=${TEMP1}`;
TEMP.setFormula(formula);
}
}
References - OFFSET

Google Sheets - Delete values of range if I have 0 in values of certain column

I would like to remove values in column B and C if in the column B I have cells with value 0.
I have tried this based on other code on this forum, with the method push to fill a empty range. The problem is when I clearContent on both arrays (values of B and C) it cleans all my values even if they have values different from zero.
I try to search the error with fontsize my second range(values of C) and it works.
My code is :
function clean0() {
var sheet = SpreadsheetApp.getActiveSheet();
var data = sheet.getRange('B:B').getDisplayValues();
var range = [];
var rangeC= [];
data.forEach(function(e, i){
if (e[0] == "0"){
range.push("B" + (i + 1));
rangeC.push("C" + (i + 1));
}
});
sheet.getRangeList(range).clearContent();
sheet.getRangeList(rangeC).clearContent();
function clean0() {
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getRange('B:C');
var data = range.getValues();
var formulas=range.getFormulas();
data.forEach(function(row, index){
//console.log('Index:'+ index + 'row' + row);
row.forEach(function(amount,index1,array){
//console.log('Row: ' + array);
var index2=row.slice(0,1);
if (row[0] == 0 && row[1]>0){
sheet.getRange(row).clear({commentsOnly: true});
//console.log('Row after: ' + row)
//data[index][index1] = "";
//console.log(data)
}
});
});
range.setFormulas(formulas)
//range.setValues(data)
}
You may try the following edited code for you, i think is much better than using push function to clear a content, although I did not use clearContent in this case, === to prevent clear range if empty:
function clean0() {
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getRange('B:D');
var data = range.getValues();
data.forEach(function(row){
if (row[0] === 0){
row[0] ='';
row[1] ='';
}
});
range.setValues(data)
}

Cannot add value into array in Google Script

I want to add the word "Flag" into column "G" or Array [6] where the corresponding row shows a value greater than 0.5 in column "E" or Array [5]. Note, that Array [6] is empty and only the script can add a value there if condition is met.
Here is my attempt but it does not add the word "Flag" into the cell.
I appreciate any help or pointer. Thanks in advance!
function test() {
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('Sheet1');
var rg=sh.getDataRange()
var vA=rg.getValues();
var g = [];
for(var i=1;i<vA.length;i++) {
g[i] = [vA[i][6]];
if(Number(vA[i][5])>0.5)g[i] = ['Flag']; {
SpreadsheetApp.getActiveSheet().getRange(2,7,g.length,1).setValues(g);
}}}
The error states:Cannot convert Array to Object[].
Here is an amended version of your code that will work if all your data is plain values and not formulas.
Please note this will overwrite any formulas.
function test() {
var ss = SpreadsheetApp.getActive();
var sh = ss.getSheetByName('Sheet1');
var rg = sh.getDataRange();
var vA = rg.getValues();
for (var i = 1; i < vA.length; ++i) {
if (Number(vA[i][5]) > 0.5) {
vA[i][6] = 'Flag';
//change value of array element
}
}
rg.setValues(vA);
//set changed values to source range
}
Edit
This checks column F and makes changes to column G.
It will not overwrite the formulas in column F, but it will overwrite any formulas in column G.
function test() {
var ss = SpreadsheetApp.getActive();
var sh = ss.getSheetByName('Sheet1');
var lastRow = sh.getLastRow();
var checkRg = sh.getRange('F2:F' + lastRow);
var flagRg = sh.getRange('G2:G' + lastRow);
var checkVa = checkRg.getValues();
var flagVa = flagRg.getValues();
for (var i = 0; i < checkVa.length; ++i) {
if (Number(checkVa[i][0]) > 0.5) {
flagVa[i][0] = 'Flag';
//change value of array element
}
}
flagRg.setValues(flagVa);
//set changed values to source range
}
Not sure what is in columns A thru F and beyond G.
for(var i=1;i<vA.length;i++) {
if(Number(vA[i][5])>0.5) vA[i][6] = 'Flag';
}
SpreadsheetApp.getActiveSheet().getDataRange.setValues(vA);
Or if you only want to replace G.
var g = [];
for(var i=1;i<vA.length;i++) {
g[i-1] = [vA[i][6]]; // Notice its an array
if(Number(vA[i][5])>0.5) g[i-1] = ['Flag']; // Notice an array again
}
SpreadsheetApp.getActiveSheet().getRange(2,7,g.length,1).setValues(g);

IF Function - Google Scripts - multiple criteria

I'm trying to run an IF function to match the date in the first column to "last month" and the date in the last column to "newest date" and copy and paste all of the rows matching this criteria (excluding the first and last column) to the bottom of the list.
This is the script I'm running and it isn't finding any matches when I know for a fact there are at least 100 rows matching this criteria:
function myFunction() {
var MCS = SpreadsheetApp.openById('[ID REMOVED FOR THIS Q]');
var MRB = MCS.getSheetByName('Media Rates Back');
var MRBrange = MRB.getRange(1,1,MRB.getLastRow(),1).getValues();
var dest = MRBrange.filter(String).length + 1;
var LM = new Date();
LM.setDate(1);
LM.setMonth(LM.getMonth()-1);
var LMs = Date.parse(LM);
var Datenew = MRB.getRange(MRB.getLastRow(),MRB.getLastColumn()).getValue();
var Datecol = MRB.getRange(1,6,MRB.getLastRow(),1).getValues();
var Datenews = Date.parse(Datenew);
for(var i=0; i<MRBrange.length; i++) {
if(Date.parse(MRBrange[i])==LMs && Date.parse(Datecol[i])==Datenews ) {
var NewRange = MRB.getRange(i,2,(MRB.getLastRow()-i),5);
var NewRangeV = NewRange.getValues();
var destination = MRB.getRange(MRB.getLastRow()+1,2);
Logger.log(NewRange);
NewRange.copyTo(destination);
}else{
Logger.log(i);
}
}}
Any help would be appreciated!
Rather than get the columns as separate ranges, I would get the entire range as one array, then loop over that and check the two columns.
I'm also assuming your values are formatted as dates in the Sheet, in which case you don't need to use Date.parse(), and that your actual date logic is correct.
You can try using the debugger and set a breakpoint at the IF, so you can check the values it is comparing. or put a Logger.log call to list your comparisons.
var last_month_column = 1;
var newest_date_column = MRB.getLastColumn();
var MRBrange = MRB.getRange(1,1,MRB.getLastRow(),newest_date_column).getValues();
for(var row in MRBrange) {
if(MRBrange[row][last_month_column]==LMs && Datecol[row][newest_date_column] ==Datenews ) {
/* your copy logic here */
}else{
Logger.log(i);
}
}
I think the problem may be that MRBrange is a 2d Array. So I used another loop to convert it to a 1d array.
function myFunction() {
var MCS = SpreadsheetApp.openById('[ID REMOVED FOR THIS Q]');
var MRB = MCS.getSheetByName('Media Rates Back');
var MRBrangeA = MRB.getRange(1,1,MRB.getLastRow(),1).getValues();//2d array
var MRBrange=[];
for(var i=0;i<MRBrangeA.length;i++)
{
MRBrange.push(MRBrangA[i][0]);//1d array
}
var dest = MRBrange.filter(String).length + 1;
var LM = new Date();//current day
LM.setDate(1);//first day of month
LM.setMonth(LM.getMonth()-1);//first day of last month
var LMs = Date.parse(LM);
var Datenew = MRB.getRange(MRB.getLastRow(),MRB.getLastColumn()).getValue();
var Datecol = MRB.getRange(1,6,MRB.getLastRow(),1).getValues();
var Datenews = Date.parse(Datenew);
for(var i=0; i<MRBrange.length; i++) {
if(Date.parse(MRBrange[i])==LMs && Date.parse(Datecol[i])==Datenews ) {
var NewRange = MRB.getRange(i,2,(MRB.getLastRow()-i),5);
var NewRangeV = NewRange.getValues();
var destination = MRB.getRange(MRB.getLastRow()+1,2);
Logger.log(NewRange);
NewRange.copyTo(destination);
}else{
Logger.log(i);
}
}}

How can I merge Same Range from each file in a folder onto a master sheet?

I modified the code this way... it gathers data from all the sheets and finds only the rows that have data, BUT now I am having a problem modifying the range with each pass so that it is equal to the number of rows that do have value (found with (values[row][0] != '')). I have put a ??? in the spot where I am trying to have a variable height.
function getAllData() {
var folder = DocsList.getFolderById("folderid");
var contents = folder.getFiles();
Logger.log("file length: " + contents.length);
var file;
var data;
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Base")
sheet.clearContents();
var numOfFiles = contents.length;
for (var i = 0; i < numOfFiles; i++) {
file = contents[i];
Logger.log("count: " + i);
var theFileType = file.getFileType();
Logger.log("theFileType: " + theFileType);
if (theFileType==DocsList.FileType.SPREADSHEET) {
var sheet2 = SpreadsheetApp.open(file).getSheetByName("Sheet 1");
var lastLine = sheet2.getLastRow();
var values = sheet2.getRange('A3:J').getValues();
var formulas = sheet2.getRange('A3:J').getFormulas();
var data = [];
for(var row = 0 ; row < (values).length ; row++){
var lastrow = sheet.getLastRow()+1;
if (values[row][0] != '') {
for(var col = 0 ; col < formulas[row].length ; col++){
if(formulas[row][col] != '')
{values[row][col] = formulas[row][col]};
data.push(values[row]);}
if(data.length > 0)
sheet.getRange(lastrow, 1, ???, data[0].length).setValues(data);
}
}
};
}}
You are using getValue() as opposed to getValues() (With a letter "s" on the end)
var onecell = posheet.getRange('B4').getValue();
The documentation states:
getValue() - Returns the value of the top-left cell in the range.
The parameter for getRange() is kind of tricky and not well documented.
For example this:
getRange(2, 3, 6, 4)
gets a range from C2 to G8. Figure that out. The first number is the number 2, which is for the row 2. The second number is 3, for the third column (which is C). The third and fourth numbers are relative to the first two numbers.
Also, you are using: appendRow([array]) which uses an array for the parameter. So you must make sure that the data is in the form of an array, or use something else.
Here is the link for getValues:
Google Documentation - getValues
The example is this code:
// The code below will get the values for the range C2:G8
// in the active spreadsheet. Note that this will be a javascript array.
var values = SpreadsheetApp.getActiveSheet().getRange(2, 3, 6, 4).getValues();
Logger.log(values[0][0]);
Here is code that seems to work:
function getAllData() {
var folder = DocsList.getFolderById("Your file ID");
var contents = folder.getFiles();
Logger.log("file length: " + contents.length);
var file;
var data;
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1")
sheet.clearContents();
sheet.appendRow(["Value from Sheet One", "Range of values from Sheet Two"]);
var numOfFiles = contents.length;
for (var i = 0; i < numOfFiles; i++) {
file = contents[i];
Logger.log("count: " + i);
//Reset to null on every iteration
var onecell = null;
var theRange = null;
var theFileType = file.getFileType();
Logger.log("theFileType: " + theFileType);
if (theFileType==DocsList.FileType.SPREADSHEET) {
var sheet1 = SpreadsheetApp.open(file).getSheetByName("Sheet1");
var sheet2 = SpreadsheetApp.open(file).getSheetByName("Sheet2");
// The code below will get the values for the range A3:A9
// in the active spreadsheet. Note that this will be a javascript array.
onecell = sheet1.getRange('B4').getValue();
theRange = sheet2.getRange(1,3,1,6).getValues();
Logger.log('onecell: ' + onecell);
Logger.log('onecell[0][0]: ' + onecell[0][0]);
Logger.log('theRange: ' + theRange)
Logger.log('theRange[0][0]: ' + theRange[0][0])
var multipleValues = [theRange[0][0], theRange[0][1], theRange[0][2], theRange[0][3], theRange[0][4]];
Logger.log('multipleValues: ' + multipleValues);
sheet.appendRow([onecell, "'" + multipleValues]);
};
}
}
In the first column, it only enters one value into the sheet cell. In the second column, the cell gets multiple values put into it from the row. In other words, and entire rows values, and combined and put into one cell. I think that's what you want from the code.
If you try to put an array into a spreadsheet cell, instead of showing the array of values as text, it shows something like an object. So I put a quote in front of the values so the cell formatting would default to text.

Categories