I would like to create a custom function to count the number of strikethrough text. Would it be possible to create a script to perform this operation?
For the moment, I managed to create a script that counts the cell number with the strikethrough text, but I have to modify my script each time for a different range, and I can not create a custom function that count the number of cells that have the text strikethrough.
Here is my script:
The result is in the L20 and the control in the script
// Count the number of cells that are barred
function fontLine() {
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var plage = "E4:E11"
var range = sheet.getRange(plage);
var x = 0;
for (var i = 1; i <= range.getNumRows(); i++) {
for (var j = 1; j <= range.getNumColumns(); j++) {
var cell = range.getCell(i, j);
if (cell.getFontLine() === 'line-through')
x++;
}
}
sheet.getRange("L20").setValue(x);
return x;
}
It's not possible to use setValue in a custom function but, as you already figured out, it's possible to get the result you are looking for by using a "regular script" (running it from a menu, by clicking an image, from the script editor, etc.)
Reference
https://developers.google.com/apps-script/guides/sheets/functions
Modifications of your code
It now uses getActiveSheet() to obtain the sheet from where the function is called, and getDataRange() to obtain the whole range in the sheet where there is data.
It uses getFontLines() on the whole range instead of iterating cell-per-cell and calling getFontLine() to every single one of them. This results in a way more efficient code (see: Best Practices - Minimize calls to other services).
Removed setValue() call. If you want the result of this custom function to be placed in the L20 cell, simply modify its contents for this formula: =fontLine(). The value returned by the custom function will be set as the cell's value.
// Count the number of cells in the sheet that are barred
function fontLine() {
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getDataRange();
var fontLines = range.getFontLines();
var x = 0;
for (var i = 0; i < fontLines.length; i++) {
for (var j = 0; j < fontLines[i].length; j++) {
if (fontLines[i][j] === 'line-through')
x++;
}
}
return x;
}
Example execution
Disclaimers
Custom functions are executed upon writing them into a cell. Afterwards, their result is cached so that less resources are used. That means that if you add or remove strike-through's from your sheet, you will have to remove and re-insert the custom function in your cell.
Related
I'm trying to use a script that automatically creates divisions on a spreadsheet. It receives as a value the number of times it has to create the same category of division. Each division/label it's composed of a merge of 6 cells in the same line.
I'm trying to make it work by using getLastRow as a base of the placement of the label, but I can't make it work it out with the merge.
Basically what I'm doing is:
function resumo() {
let ss = SpreadsheetApp.getActive();
let resumo = ss.getSheetByName("Resumo");
let numEntrada = resumo.getRange("c12").getValue();
criaParcela();
function criaParcela() {
for (i = 0;i < numEntrada; i++){
var fLine = resumo.getLastRow();
var bcell = (fLine+1);
var fcell = (fLine+6);
resumo.getRange(fcell,1).setValue("Entrada");
resumo.getRange(bcell,1,6,1).mergeVertically();
}
}
}
As you can notice, I'm not professional programmer.
Assuming that this is the end result you are looking for:
You can try the below script:
function doStuff() {
let spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
let sheetResumo = spreadsheet.getSheetByName("Resumo");
let numEntrada = sheetResumo.getRange("C12").getValue();
for (let i = 0; i < numEntrada; i++) {
let lastRow = sheetResumo.getLastRow() + 1;
sheetResumo.getRange(lastRow, i+1, 6, 1).mergeVertically();
}
}
The most notable changes that have been done to your script are the following:
+ using only one function;
Apps Script does not accept a function within a function in the way you had tried to use it.
+ getRange method is getting the proper parameters in order to perform the merge operation successfully;
As you can see, the i index is needed if you want to merge the same cells numEntrada multiple times.
Reference
Apps Script Sheet Class - getRange(row, column, numRows, numColumns).
I have a deleteEachRow function that loops through a sheet and delete Rows that have a particular Column Value.
This works fine and was hoping to modify it in such a way that it loops through a multile sheets in the work-book and also delete rows based on multiple Column Values.
The deleteRow() script
//GLOBALS
var SS = SpreadsheetApp.openById("sheetID");
var SHEET = SS.getSheetByName("Sheet1");
var RANGE = SHEET.getDataRange();
var DELETE_VAL = "abc";
var COL_TO_SEARCH = 4; // The column to search for the DELETE_VAL (Zero is first)
function deleteEachRow(){
var rangeVals = RANGE.getValues();
//Reverse the 'for' loop.
for(var i = rangeVals.length-1; i >= 0; i--){
if(rangeVals[i][COL_TO_SEARCH] === DELETE_VAL){
SHEET.deleteRow(i+1);
};
};
};
What I have tried..
var SHEET = SS.getSheetByName(["Sheet1", "Sheet2"]);
var DELETE_VAL = ["abc","DEF"];
function deleteEachRow(){
var rangeVals = RANGE.getValues();
//Reverse the 'for' loop.
for(var i = rangeVals.length-1; i >= 0; i--){
for(var i=0; size = DELETE_VAL.length; i < size; i++){
if(rangeVals[i][COL_TO_SEARCH] === DELETE_VAL[i]){
for(var i=0; size = SHEET.length; i < size; i++){
SHEET[i].deleteRow(i+1);
};
};
};
};
};
Which completes executing from my logs, but does not actually work. I may have murdered some logic here, please pardon me, I am new to .gs/.js.
Thanks for your anticipated response.
Issue : You're passing array to getSheetByName, whereas as per documentation it accepts String only. i.e. Name of the single sheet you want to fetch.
https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet#getsheetbynamename
So you can modify your function to take sheet name as input and then delete rows in that sheet. Then call your function with desired sheet names. Something like this:
var spreadSheet = SpreadsheetApp.openById("sheetID");
var DELETE_VAL = "abc";
var COL_TO_SEARCH = 4; // The column to search for the DELETE_VAL (Zero is first)
function deleteEachRow(sheetName){
var SHEET = spreadSheet.getSheetByName(sheetName);
var RANGE = SHEET.getDataRange();
var rangeVals = RANGE.getValues();
// existing logic
};
// Invoke deleteEachRow() for each sheet you want to delete the rows
["Sheet1", "Sheet2"].forEach((sheetName) => deleteEachRow(sheetName));
Umair is right, there was a simply error in the first line. But I'd want to add that the sheet.deleteRow(row) is not the best practice in case if there are many rows to delete. This command is quite time consuming.
If you have more than dozen rows to delete it's better to grab all data from a sheet (or range) var data = range.getValues(), clear the sheet (or the range), to process the array inside the script and refill the sheet back with new data new_range.setValues(array). It will work much faster.
I am trying to migrate from Microsoft Excel to Google sheets in order for the company to work from any work station.
I want to compare two ranges of data, one in one column and the other in another column.
If the cell value in the first range equals the cell value in the second range, then I want it to...
Add the value from the second range to the first range in the matching cell and continue checking the rest of the range.
I have done this easily in VBA but I am having a hard time doing it in Goole Sheets.
For now the code in Google Sheets does the following:
If it finds a matching value between the two ranges it adds the value to the every single cell in the first range.
Here is what my code looks like:
function myFunction() {
var pr = SpreadsheetApp.openById("my spreadsheet");
var sheet = pr.getSheetByName("sheet1");
var sheet1 = pr.getSheetByName("sheet1");
var cell = sheet.getRange('J3:J95');
var cell1 = sheet1.getRange('C3:C1125');
if(cell1.getValue()===cell.getValue()) {
cell1.offset(0, 2).setValue(cell1.offset(0, 2).getValue() + cell.offset(0, 1).getValue())
}
}
How can I make this add value only to the one matching cell and not the entire range?
You can achieve this using for-loops:
An example:
Before running script:
Script:
function myFunction() {
var sheet = SpreadsheetApp.getActiveSheet();
var keys = sheet.getRange('A1:A5').getValues();
var values = sheet.getRange('B1:B5');
for (var i = 0; i < keys.length; i++){
isMatch(keys[i], values);
}
}
function isMatch(key, values) {
for(var i=0; i< values.getNumRows(); i++){
var sample = values.getValues()[i];
if (key[0] == sample[0]) {
values.getCell(i+1, 1).offset(0,1).setValue(values.getCell(i+1, 1).offset(0,1).getValue()+key[0]);
}
}
}
After running script:
Im trying to create a simple script that firstly checks (all cells in row 3 starting from column 3) for whether they contain a name different from the available sheets and if so create a new one. If not go to the next cell down. Preferably until the row is empty but I didnt get that far. Currently I have
var row = 3; //Global Variable
function Main() { // Main Function
Activate();
GetNames();
}
function Activate() { // Initialize
var get = SpreadsheetApp.getActiveSpreadsheet();
var import = get.getSheetByName("Import");
}
function GetNames() { // Get Names and check for existing Sheets
var ss = SpreadsheetApp.getActiveSpreadsheet();
var import = ss.getSheetByName("Import");
var sheet = ss.getSheets()[0];
for (i = 0; i < 1000; i++) { // Loop which I think is broken
var names = import.getRange(row,3).getValue();
if (import.getSheetByName(names) == null) {
import.insertSheet(names);
import.activate();
}
row + 1;
}
}
And here is the Data
It succeeds to add the first sheet but fails to continue in the loop I think.
As you will probably see I'm very new to this and any help would be appreciated.
You probably wanted to increase row by 1 here:
row + 1;
But you're not. row + 1 is just an expression with a value (4, in your example, because row remains 3 throughout). What you would need is the statement
row = row + 1;
But if this is all that you're using the global variable row for, you don't need it at all. Just use the loop variable i that's already counting from 0 to 1000. You probably want something like import.getRange(i+3,3).
I am trying to write a script in Google Apps Script that takes cell information from one sheet and copies it to another sheet, both for just grabbing certain columns to display on the second sheet and also a condition based on the values inside cells in a certain column. Here is what I have so far:
function onMyEdit() {
var myMaster = SpreadsheetApp.openById("xxxxx");
var masterSheet = myMaster.setActiveSheet(myMaster.getSheets()[0]);
var myNames = SpreadsheetApp.openById("xxxxx");
var namesSheet = myNames.setActiveSheet(myNames.getSheets()[0]);
var row1 = masterSheet.getRange(1, 1, masterSheet.getLastRow(), 1);
var rowV = row1.getValues();
var firstArray = masterSheet.getDataRange().getValues();
var dataList = [];
for (var i = 1; i < rowV.length; i++) {
dataList.push(firstArray[i][0]);
dataList.push(firstArray[i][1]);
dataList.push(firstArray[i][2]);
dataList.push(firstArray[i][3]);
}
for (var j = 0; j < rowV.length - 1; j++) {
namesSheet.getRange(2, j + 1, 1, 1).setValue(dataList[j]);
}
}
So as of now it only works on one row, starting from the second row (to allow for column headers). And I suppose when I want to grab rows conditionally based on cell data, I will use an 'if' statement for the condition inside the 'for' loop, but I want the data to copy to the next available row in both sheets. I suppose I'd use something like:
' getLastRow + 1 '
or something like that. I need this code to be as efficient as possible because of the amount of data and its purpose. I am pretty new to programming so please explain in detail, and thanks again.
I'm not sure I understood exactly what you wanted to do but -from what I understood- this code snippet should give you a better way to start with...
(I added a few comments to explain in the code itself)
function onMyEdit() {
var myMaster = SpreadsheetApp.openById("MasterSheet ID");
var masterSheet = myMaster.getSheets()[0]; // get 1rst sheet
var myNames = SpreadsheetApp.openById("NamesSheet ID");
var namesSheet = myNames.getSheets()[0]; // get 1rst sheet
var firstArray = masterSheet.getDataRange().getValues();
var dataList = [];
for ( r = 1; r < firstArray.length; r++) { // iterate the first col of masterSheet
if(firstArray[r][0]=='some condition'){ // if value in the first column == 'some condition get the second column cell in the new array (here you could change what you want to get)
dataList.push([firstArray[r][1]])
}
}
Logger.log(dataList)
if(dataList.length>0){
namesSheet.getRange(1,namesSheet.getLastColumn()+1,dataList.length,1).setValues(dataList);//copy data in a column after last col
}
}