Delete selected rows script - google-apps-script

I have this script to delete row that are checked with a checkbox.
function deleterows() {
var sheet = SpreadsheetApp.getActiveSheet();
sheet.showRows(1, sheet.getMaxRows());
var values = sheet.getRange('K1:K50').getValues();
values.forEach( function (r, i) {
Logger.log(r);
if (r[0])
sheet.deleteRows(i+1);
});
}
But it does not work properly - when I select multiple rows with the checkboxes - it deletes part of selected and part of the rows that follow selection
screenshot
I am new the JavaScripts so can someone help me please

By one hand, according to https://developers.google.com/apps-script/reference/spreadsheet/sheet#deleterowsrowposition-howmany, deleteRows require two parameters but your script is passing only one.
By the other hand, every time that a row is deleted the row number of the below rows changes, so it's better to iterate backwards. For more details see this other Q/A Deleting rows in google sheets using Google Apps Script

Related

Google Sheets: Action Based on Birthday

I'm trying to send myself either an email or copy the row to a new sheet when it's someone's birthday or hire date anniversary. Copying the line to a new sheet would allow me to use zapier to notify me of the update. Either would work. The sheet uses a form to collect data.
I've built a few scripts but nothing that had to do with dates. I'm just struggling with this one and have tried a few examples I could find with no luck.
Here is this sheet. It's view only so just let me know if you need more access.
I understand that you want to replicate your form responses Sheet in another Sheet (let's call it Zapier Sheet) automatically each time that a new form response is added. You can achieve that goal developing an Apps Script code that runs at each form response. In that case you can use a code similar to this one:
function so62400514() {
var formSheet = SpreadsheetApp.openById(
'{FORM SHEET ID}').getSheets()[0];
var zapierSheet = SpreadsheetApp.openById(
'{ZAPIER SHEET ID}').getSheets()[0];
var formData = formSheet.getRange(1, 1, formSheet.getLastRow(), formSheet
.getLastColumn()).getValues();
var zapierData = zapierSheet.getRange(1, 1, zapierSheet.getLastRow(),
formSheet.getLastColumn()).getValues();
var recorded = false;
for (var fr = 0; fr < formData.length; fr++) {
for (var zr = 0; zr < zapierData.length; zr++) {
if (formData[fr].toLocaleString() == zapierData[zr].toLocaleString()) {
recorded = true;
}
}
if (recorded == false) {
zapierSheet.appendRow(formData[fr]);
} else {
recorded = false;
}
}
}
This code will first open both sheets (using SpreadsheetApp.openById() and Spreadsheet.getSheets()) to select the data with Sheet.getRange (setting boundaries with Sheet.getLastRow() and Sheet.getLastColumn()) and reading it using Range.getValues(). After that operation the data will get iterated using the property Array.length as the perimeter. The iteration compares each row from the form Sheet to every row of the zapier sheet (to accomplish that, I first parsed the row as a string with Date.toLocaleString()). If the form row is found in the zapier sheet, the boolean recorded will flag to true. After every row on the zapier sheet gets compared to the form row, the code will write it down on the zapier sheet based on the boolean flag.
As explained in the previous paragraph, this code will take the form sheet rows not present in the zapier sheet; and paste them on the zapier sheet. I used this approach to prevent missing any row (as it could happen when simultaneous users answer the form all at once). To make this fire automatically you'll need to set up an installable trigger with these settings:
As an example, let's say that we have these form responses:
And our initial sample zapier sheet looks like this one below. Please, notice how several past rows are missing;
After running the script (as it will do automatically) this would be the result:
I suggest running the script manually for an initial setup. If the timestamps diverge, please check if both spreadsheets share time zones. Don't hesitate to ask me further questions to clarify my answer.

Automate dynamic IMPORTRANGE in google sheets

I am building a multi-section questionnaire (3 in total) and I want to have 4 sheets to hold the data (one master and one for each section).
How would I send data to another sheet from the master spreadsheet when a new row is added to the Master Sheet and make it dynamic so it does not pull the same row everytime?
I have found this script online:
script link
but it is for moving data between sheets unfortunately. Could it be remodeled?
Thanks!
Perhaps the setFormula class can help you, it can be dynamic & you can update it with a trigger
sheet.getRange(1,1,1,1).setFormula('=IMPORTRANGE("SPREADSHEET_URL", "SHEET_NAME!'+rangevariable1inA1notation+':'+rangevariable2inA1notation+'")');
Make sure you get the range you want, and the notation of those 2 variables to get the dynamic range:
var rangevariable1inA1notation = sheet.getRange(1,1).getA1Notation(); // for example, instead of 1,1 it could be your own variable
var rangevariable2inA1notation = sheet.getRange(2,2).getA1Notation(); //for example
To send values to another sheet (besides from your main one), you can use the onFormSubmit function, it will be triggered every time the form is submitted and with its event object, which contains the information from your form, you will be able to pass those values into the other sheets as you want.
// This will be triggered every time the form is submitted
function onFormSubmit(e) {
// Get all sheets in the active spreadsheet
var sheetsArr = SpreadsheetApp.getActiveSpreadsheet().getSheets();
// Get the second sheet
var slaveSheet1 = sheetsArr[1];
// Get the row where the values will be inserted
var rowVals = slaveSheet1.getLastRow() + 1;
// The number of cols where you will puth the values
var numberOfCols = e.values.length;
// Set values that came from the form
slaveSheet1.getRange(rowVals, 1, 1, numberOfCols).setValues([e.values]);
}
Be careful with the installable triggers Restrictions.

Doesn't sheet.isRowHiddenByFilter(n) work with slicers?

I am trying to use a script to do something to only the rows that are not filtered when using slicers in Google Sheets. I can't get it to work.
I have tried this:
Logger.log(SpreadsheetApp.getActiveSheet().isRowHiddenByFilter(1));
I get "false" even if the row is hidden by using a slicer (new feature in Google Sheets).
Anyone got it to work?
Edit: It works with regular filters, grammar, edited row number in function parameter. Replaced the phrase filter control (direct translation from Norwegian) to slicers, which is the correct english name for the feature.
You want to use isRowHiddenByFilter() for Slicer.
If my understanding is correct, how about this answer? Please think of this as just one of several answers.
By the update of Google side at November 6, 2019, Class Slicer was added. By this, Slicer of Spreadsheet got to be able to be managed with Google Apps Script.
In this answer, I would like to propose the method for using isRowHiddenByFilter() using Class Slicer.
Flow:
Unfortunately, even when Class Slicer was added, isRowHiddenByFilter() cannot be directly used for the Slicer. So as a workaround, the following flow is used.
Retrieve the current Slicer.
Retrieve the range, column position and the filter criteria from the Slicer.
Create new basic filter using the retrieved range, column position and the filter criteria.
Here, the filter of Slicer is copied as the basic filter.
Here, isRowHiddenByFilter() can be used for the created basic filter.
Delete the basic filter.
Usage
1. Create Slicer on Spreadsheet
Here, as a test case, please manually create new Slicer on a sheet in the Spreadsheet. This sample script supposes that only one Slicer is put in the active sheet.
2. IMPORTANT: Save Setting of Slicer
When Set current filters as default is selected (clicked), the setting of Slicer is saved. Please save the setting of the Slicer by selecting Set current filters as default. You can see Set current filters as default at the following figure.
If Set current filters as default is not selected, the setting of the current Slicer is not saved. Please be careful this. The official document can be seen at here.
3. Run sample script
When the function of myFunction() is run, the filtered values of Slicer on the active sheet can be retrieved using isRowHiddenByFilter().
function myFunction() {
var sheet = SpreadsheetApp.getActiveSheet();
var slicer = sheet.getSlicers()[0];
var range = slicer.getRange();
var filter = range
.createFilter()
.setColumnFilterCriteria(
slicer.getColumnPosition(),
slicer.getFilterCriteria()
);
var result = range.getValues().filter(function(_, i) {
return !sheet.isRowHiddenByFilter(i + 1);
});
filter.remove();
Logger.log(result);
}
In this case, when return !sheet.isRowHiddenByFilter(i + 1); is modified to return sheet.isRowHiddenByFilter(i + 1);, the hidden rows can be retrieved.
References
isRowHiddenByFilter(rowPosition)
Class Slicer
Save your slicer filtering selections
This answer adds on #Tanaike's answer.
When there are multiple slicers on the page, you cannot create new filter per slicer but instead you have to apply new filter criterias to the existing filter on the page.
function filterFromSlicer() {
var sheet = SpreadsheetApp.getActiveSheet();
var slicers = sheet.getSlicers();
slicers.forEach(slicer => {
var range = slicer.getRange();
var filter = range.getFilter() || range.createFilter();
filter.setColumnFilterCriteria(
slicer.getColumnPosition(),
slicer.getFilterCriteria()
);
});
var result = sheet.getDataRange().getValues().filter(function (_, i) {
return !sheet.isRowHiddenByFilter(i + 1);
});
Logger.log(result);
}

How to easily split large Google sheet into separate sheets of 200 rows?

I frequently deal with large spreadsheets and am looking for a way to easily split these into rows of 200.
To explain more clearly: I have a spreadsheet containing one sheet (or tab?) with 2000 rows.
Currently, I would open a new sheet (in the same work book), mark the first 200 rows and copy and paste them into a new sheet. Then I mark the next 200 rows and copy and paste them into a new sheet etc.
Is there a way of automating this process or speeding it up with a function?
Thanks for your time and apologies for my poor explanation.
this script should do the trick, just replace the numberOfLines
function myFunction() {
var numberOfLines = 10;
var sheet = SpreadsheetApp.getActiveSheet();
var data = sheet.getDataRange().getValues();
var yourNewSheet = {};
for (var i = 0; i < data.length; i++) {
var tabName = i%numberOfLines;
if(tabName == 0){
yourNewSheet = sheet.getParent().insertSheet("tab #" + i);
}
yourNewSheet.appendRow(data[i]);
}
}
You could use an ImportRange formula, like Chris Hick suggested here.
Or you may try using scripts. See more info here:
https://developers.google.com/apps-script/reference/spreadsheet/range
If you have no luck with this, please tell what you've tried so far so we could suggest any improvements.
Create a new sheet.
If your sheet has headers, click on cell A1 of your new sheet and enter =ARRAYFORMULA( -- click on your original sheet and click on the header row. The formula should complete to =ARRAYFORMULA(NameOfSheet!1:1)
In Cell A2 of your new sheet, enter =ARRAYFORMULA( -- click on your original sheet and select row 2. The formula should auto complete to =ARRAYFORMULA(NameOfSheet!2:2). You will want to modify the second number in the function to encompass as many rows as you wish to copy to the new sheet. For example, to copy 200 rows, you would use the formula =ARRAYFORMULA(NameOfSheet!2:202)
You can continue to create additional sheets -- simply duplicate the second sheet and augment the first and second numbers to copy additional sections of your sheet, for example:
=ARRAYFORMULA(NameOfSheet!203:402)
=ARRAYFORMULA(NameOfSheet!403:602), etc.

google sheets form function not updating with data input from the script

I'm having some trouble getting a function in a google sheet to calculate using values inserted by a script.
I have a google script that is adding data to a sheet based on user-inputted data from a form that the script has created. So, in the form, a user inputs their name, selects a product and some options, and the script adds this to a sheet named 'Client Data Sheet'.
I then have a different sheet which is supposed to do the math to calculate the price. My script copies all the functions from a hidden template row into the next-available row, so, for example, cell D7 contains ='Client Data Sheet'!A4, D8 contains ='Client Data Sheet'!B4 etc... These all display the correct values.
The problem is the price calculation function, in that same calculation sheet, which has a rather complex function that a previous coder wrote. This function should calculate the price of the product and options based on the data in the same sheet. It does so without calling a script, just pulling data from cells in other sheets, running some if() checks to decide whether or not to add extra costs to the total price, and adding it all up.
Problem is, it doesn't update based on the new data. It just shows 0, as though the other cells were empty, even though they now contain data updated by pulling data from another sheet which was edited by a script. If I go in to edit the function and just press enter, it re-calculates correctly, so I basically just need the function to re-evaluate based on the new data that is in the cells it's dependent on.
My theory is that it's not updating the function since I didn't directly edit the cells it's dependent on. I could try to change the awkward, huge function this other coder wrote so it pulls from the spreadsheet my script edits, rather than from cells that copy in that data, but that seems like a workaround, and is unsatisfying.
TL;DR: a function isn't updating based on data that changes in cells that copy the data from other cells which are filled by a script. Anybody have any advice for how to get the function to update?
EDIT: Ok, so if I make sure that the function that isn't updating pulls at least some data from cells that the script updates, it works. It seems that it doesn't recognize that the cell once removed has updated as well. It would be better, though if it was able to pull from the cells that pull their data from the cells the script updates. This would let other users of the sheet change the products if a customer requested a change later on without having to edit my hidden sheets that the data is pulled from.
Code:
This copies a template row in my spreadsheet that contains the math functions:
function new_client(){
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Liquidation');
if(ss.getName()=="Data" || ss.getName().indexOf("Liq")==0){
var last_row = ss.getLastRow();
var last_col = ss.getLastColumn();
ss.insertRowsAfter(last_row, 1);
var template_row = 4;
var copy_range = ss.getRange(template_row, 1, 1, last_col);
var paste_range = ss.getRange(last_row+1, 1, 1, last_col);
copy_range.copyTo(paste_range, {contentsOnly:false});
paste_range.getCell(1,1).setValue("NO");
SpreadsheetApp.flush();
}
}
This copies data from the form into a different spreadsheet, which the sheet edited above pulls data from:
var clientSheet = SpreadsheetApp.getActive().getSheetByName('Client Data Sheet');
clientSheet.insertRowsAfter(clientSheet.getMaxRows(), 1)
var lastRow = clientSheet.getLastRow() + 1;
var lastColumn = clientSheet.getLastColumn();
var destRow = clientSheet.getRange(lastRow, 1, 1, lastColumn);
var column = 1;
for (var key in user) {
if (user.hasOwnProperty(key)) {
destRow.getCell(1, column).setValue(user[key]);
column++;
}
}
So, for example, after this code is run, the Client Data Sheet contains a cell, B4, which now contains the name of the chosen product, "foo". The Liquidation sheet has a cell let's call it A5 that contains the function ='Client Data Sheet'!B4, as well as another cell which has the price calculation function: =if(A5="foo", 100, 0)
When the script above inserts the values from the form, the cell B4 in Client Sheet and the cell A5 in the Liquidation sheet will contain the right value, but the cell with the calculation function =if(B4="foo", 100, 0) will not update.