The names of sheets are changed when users use different languages - google-sheets

I use the formula {'Form Responses 1'! A1: G; 'Form Responses 2'! A2: G; ...} to get data from all sheets into one sheet. However, when the user uses a language other than English, the sheets' names are changed, and the related formulas are corrupted.
Can anyone help me to solve this problem, thank you in advance.
The code:
function copy() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var SpssId = ss.getSheetByName('title').getRange('AH6').getValue();
var destinationSpss = SpreadsheetApp.openById(SpssId);
for (var i = 1; i < destinationSpss.getNumSheets(); i++) {
var index = destinationSpss.getSheetByName('index');
index.getRange(i+1,2,1,4)
.setFormulas([["='Form Responses "+i+"'!B2","='Form Responses "+i+"'!D2","='Form Responses "+i+"'!C2","='Form Responses "+i+"'!E2"]]);
}}

Thanks for providing the code. It was very helpful to see what you had so far.
Can we assume that index is the only sheet in the destination spreadsheet that is not one of the form response sheets? Judging from your code, it seems like we can assume this.
So then we can just use Spreadsheet.getSheets() to get an array of all the sheets and Sheet.getName() to get their names, regardless of language:
function copy() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var SpssId = ss.getSheetByName('title').getRange('AH6').getValue();
var destinationSpss = SpreadsheetApp.openById(SpssId);
var sheets = destinationSpss.getSheets() // Get an array of the sheets
var index = destinationSpss.getSheetByName('index');
for (var i = 0; i < sheets.length; i++) {
var sheetName = sheets[i].getName();
if (sheetName === 'index') break;
index.getRange(i + 1, 2, 1, 4)
.setFormulas([[`="${sheetName}"!B2`,`="${sheetName}"!D2`,`="${sheetName}"!C2`,`="${sheetName}"!E2`]])
}
}
(I'm using template literals to format the formula strings, which just makes the code a little cleaner, but this is not necessary of course.)
Can you always assume that the index sheet is at index 0? I'm not sure if you can, so I would expand your for-loop to include i = 0 and add the check if (sheetName === 'index') break; to break if you're on the index sheet.
As an aside, you'll see I also moved var index = destinationSpss.getSheetByName('index'); outside of the for-loop. The App Scripts API can be slow for certain operations, and since you're referencing the same index sheet for every iteration, your code will be faster if you call for this index sheet just once.

Related

Formula to ignore hidden columns

I am trying to count visible columns in a spreadsheet with no luck. I've been trying using SUBTOTAL function but it's applied to hidden/visible rows only. I also tried working with CELL("width") function, but it doesn't return 0 when a cell is hidden
Is there any other option to ignore hidden columns in a count formula?
You can definitely create your own custom function using Google Apps Script.
For example, the following function counts the number of visible columns in your active sheet:
function countVisibleColumns() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet()
var n_cols = sheet.getMaxColumns();
var hidden_cols = []
var cnt = 0;
for (var i=1; i<=n_cols ; i++) {
if ( sheet.isColumnHiddenByUser(i) ){
continue;}
else {cnt +=1} }
Logger.log(cnt)
return cnt;
}
You just need to click on Tools => Script editor and then copy the aforementioned code into a blank script. Then you can directly use the function as a formula in the google sheet like =countVisibleColumns().
See screenshot attached for more information.

Countif Indirect Google Sheets

I am trying to write a formula that counts the number of times the number 1 appears in cell F1 of all my sheets. My sheets have varying names (18-0100, 18-0101, 18-0102...). I tried the following formula:
=COUNTIF(INDIRECT("'"&"'!F1"),"=1")
It acts unpredictably. It will only return 1 even if it should be more than 1. And when I try to start trying to count 2 instead of 1 it returns 0 and not the correct number.
What am I doing wrong?
Your formula counts only the current sheet.
To get them all you need to enter all sheet names:
The formula for each sheet is:
=(INDIRECT("'"& sheet_name &"'!F1")=1)*1
You can leverage a Google Apps Script to pull this off as well.
From your spreadsheet, go to Tools > Script Editor. Add the code below, then save. The function saved there will be accessible by all other Google apps, including your spreadsheet. You can then use the name of the function in your spreadsheet like you would with any other formula.
function OccurrencesInSheetNames(str) {
var c = 0;
var regExp = new RegExp('[' + str + ']', 'g');
var sheets = SpreadsheetApp.getActiveSpreadsheet().getSheets();
var sheetnames = [];
for (var i=0; i<sheets.length; i++) {
sheetnames.push([sheets[i].getName()]);
}
for (var i=0; i<sheetnames.length; i++) {
var name = sheetnames[i].toString();
var count = (name.match(regExp) || []).length;
c += count;
}
return c;
}
Now in your cell F1, place the following formula:
=OccurrencesInSheetNames(TEXT(1,0))
You can also replace the 1 in the above formula with a cell reference, if that works better for your needs. (e.g. =OccurrencesInSheetNames(TEXT(C5,0)) if you want to search through your sheet names for the integer number found in cell C5.)
Here's a demo spreadsheet for you to try out.

How to use one conditional formatting in Google Sheets to all sheets at once?

I made some 'conditional formatting' in Google Sheets for one sheet but I need to apply to others. There are about 45 tables and I really don't want to copy-paste it. Can anyone help me with that?
A quick solution would be to do the following:
Record a macro of how you apply the conditional formatting on the first sheet
Edit the macro so that it loops through each sheet (see code example below)
Be sure to do this on a copy of the original sheet first in case there are any issues
Code:
function autoConditionalFormat() {
// Counts how many sheets there are
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheets();
numSheets = sheets.length;
// Loop to get name of each tab (sheet)
var tabNames = new Array()
for (var i=0; i<numSheets; i++) tabNames.push( [ sheets[i].getName() ] )
// Loops through each sheet
for (var i = 0; i < numSheets; i++) {
// Applies some conditional formatting to each sheet
SpreadsheetApp.setActiveSheet(ss.getSheetByName(tabNames[i]));
// Insert what your macro recorded here:
var spreadsheet = SpreadsheetApp.getActive();
spreadsheet.getRange('A2').activate();
var conditionalFormatRules = spreadsheet.getActiveSheet().getConditionalFormatRules();
conditionalFormatRules.push(SpreadsheetApp
.newConditionalFormatRule()
.setRanges([spreadsheet.getRange('A2')])
.whenCellNotEmpty()
.setBackground('#B7E1CD')
.build());
spreadsheet.getActiveSheet()
.setConditionalFormatRules(conditionalFormatRules);
}
}
}

Trying to write to a Google Sheet opened with "openById"

Project description: Begin with a roster spreadsheet with multiple tabs (one for each group), each tab with a list of members for rows. Columns are rehearsal or program dates. Second sheet contains scanned attendance records. Open the attendance record sheet and read each record. One column contains tab name of sheet on the roster sheet. Change to that sheet and search for the scanned member. Mark the column matching the date with an X to denote attendance. Mark the scanned attendance record with an X to denote processed.
Everything is working save the final writing of the X to the sheet opened by ID. I can't figure out the syntax to update the appropriate row/col cell.
I'd appreciate some help. Thanks!
function processAttendanceRecords(){
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var ss = SpreadsheetApp.openById("1234567");
var data = ss.getDataRange().getValues();
for (var i = 1; i < data.length; i++) {
var rhrsDate = Utilities.formatDate(data[i][3], "PST", "M/d");
sheet.setActiveSheet(sheet.getSheetByName(data[i][1]));
var members = sheet.getDataRange().getValues();
var col = members[0].indexOf(rhrsDate);
for (var j = 1; j < members.length; j++) {
if (data[i][6] == members[j][0] && data[i][7] == members[j][1]) {
SpreadsheetApp.getActiveSheet().getRange(j+1,col+1).setValue('X');
SpreadsheetApp.getActiveSheet(ss).getRange(i+1,9).setValue('X');
}
}
}
}
If you are accessing spreadsheet by ID then it is good practice to mention the sheet name or else it will take the first sheet by default.
var ss = SpreadsheetApp.openById("123456").getSheetByName("Sheet1");
Since you haven't defined the sheet name you can't use getRange(Row, Column). So your need to mention the sheet name like above and then change the last line.
ss.getRange(i+1,9).setValue('X');

Google script to send conditional emails based on cells in google sheets

I have a google sheet that I would like to have generate an email alert when one column is greater than the other. Specifically, column F > column G. Here is what I have so far, any advice would be greatly appreciated, as I do not have much skill writing functions.
function readCell() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Watch list");
var value = sheet.getRange("F2").getValue();
var value1 = sheet.getRange("G2").getValue();
if(value>value1) MailApp.sendEmail('example#gmail.com', 'subject', 'message');
};
Currently this only attempts to compare cell F2 to cell G2. Is there a way to make the function compare the entire F column against column G, and generate an email for each individual case where Fx > Gx ?
Thank you!!
You have to loop all over the range.
first instead of getting the content of one cell you'll need to get the content of all the column:
var value = sheet.getRange("F2").getValue();
become that
var values = sheet.getRange("F2:F").getValues();
(same for value1)
then you need to create an empty table that will collect the results:
var results = [];
and now you need to loop throught all the values:
for(var i=0;i<values.length;i++){
//do the comparaison and store result if greater for example
}
then you may send the result.
all put together it give something like that:
function readCell() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Watch list");
var values = sheet.getRange("F2:F").getValues();
var value1s = sheet.getRange("G2:G").getValues();
var results = [];
for(var i=0;i<values.length;i++){
if(values[i]<value1s[i]){
results.push("alert on line: "+(i+2)); // +2 because the loop start at zero and first line is the second one (F2)
}
}
MailApp.sendEmail('example#gmail.com', 'subject', results.join("\n"));
};
If you want to trigger that function automatically you'll also need to change the way you call the spreadsheet (instead of getActive.... you'll need to use openById)

Resources