I have a 50,000+ row Google Sheet that I update every day with new data. At the end of the day I run an "IF" function that helps me determine if I want to delete that row.
I then wrote a script that looks through the sheet and deletes any row that has "DEL" in the specific Column. The problem is that since I have so many rows, the script takes too long to run. Anyone have any ideas of a more efficient way to delete/clear a row if a cell has the letters "DEL"?
function deleteRows() {
var sheet = SpreadsheetApp.getActive().getSheetByName('DEL_test');
var rows = sheet.getDataRange();
var numRows = rows.getNumRows();
var values = rows.getValues();
var rowsDeleted = 0;
for (var i = 0; i <= numRows - 1; i++) {
var row = values[i];
if (row[9] == 'DEL') { // Search cells in Column "J" and delete row if cell is equal to "DEL"
sheet.deleteRow((parseInt(i)+1) - rowsDeleted);
rowsDeleted++;
}
}
Browser.msgBox('COMPLETE');
};
You want to efficiently delete rows with DEL in the column "J".
How about using Sheets API? When Sheets API is used, several rows can be deleted by one API call. By this, the process cost will be able to be reduced. I think that there are several workarounds for your situation. So please think of this as just one of them.
When you use Sheets API, please enable Sheets API at Advanced Google Services and API console. You can see about how to enable Sheets API at here.
Modified script:
function deleteRows() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('DEL_test');
var rows = sheet.getDataRange();
var values = rows.getValues();
var spreadsheetId = ss.getId();
var sheetId = sheet.getSheetId();
var reqs = values.reduceRight(function(ar, e, i) {
if (e[9] == 'DEL') {
ar.push({"deleteDimension":{"range":{
"sheetId": sheetId,
"dimension": "ROWS",
"startIndex": values.length - i - 1,
"endIndex": values.length - i,
}}});
}
return ar;
}, []);
Sheets.Spreadsheets.batchUpdate({"requests": reqs}, spreadsheetId);
Browser.msgBox('COMPLETE');
}
References:
spreadsheets.batchUpdate
DeleteDimensionRequest
If I misunderstood your question, please tell me. I would like to modify it.
Edit:
If you don't want to use Sheets API and you want to use clearContent(), how about this sample script? The flow of this sample script is as follows.
Retrieve all values.
Retrieve only the rows which have no "DEL" in the column "J".
Put the values.
Sample script:
function deleteRows() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('DEL_test');
var rows = sheet.getDataRange();
var values = rows.getValues();
sheet.clearContents();
var val = values.filter(function(e) {return e[9] != 'DEL'});
sheet.getRange(1, 1, val.length, val[0].length).setValues(val);
Browser.msgBox('COMPLETE');
}
Note:
I'm not sure about your actual Spreadsheet. So if this was not the result you want, can you provide a sample Spreadsheet? By this, I would like to modify the script.
For the time being I have removed hope of using "deleteRow" as it looks to be a slow-to-operate function. That has lead me to using this script which works for the time being. Thank you to everyone gave me their time.
var spreadsheet = SpreadsheetApp.getActive().getSheetByName('DEL_test');
var rows = spreadsheet.getDataRange();
var numRows = rows.getNumRows();
var values = rows.getValues();
var lr = spreadsheet.getLastRow();
for (var i = 0; i < numRows; i++) {
if (values[i][9] == 'DEL') {
spreadsheet.getRange(i+1, 1, 1, 10).clearContent();
}
}
Related
Currently, I have a basic spreadsheet: https://docs.google.com/spreadsheets/d/1nlLdD48PfqrhyQzmJrhx_Tlw73C194YUqKeRkXoCOLg/edit?usp=sharing, and I have made a script that flattens the values of the formulas in the spreadsheet:
function freezeValues() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Sheet1'); // or whatever name
var range = sheet.getRange("A2:C"); // assuming data appears in A2-C
range.copyTo(range, {contentsOnly: true});
}
The issue with my current script is that it flattens all formula, even if the row is hidden by the filter, is there a way I can edit my script so that it will only flatten visible cells?
I found this operation for google app script isRowHiddenByFilter, but I am not sure how I could put it into my current script.
P.S. I will likely use this for different filters, so am not looking for a workaround. For example, if you look at my example spreadsheet, I wouldn't want the app script to specify "background-colour = white" etc)
You can loop through all rows and query either it is hidden before copying
Sample
function freezeValues() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Sheet1'); // or whatever name
var firstRow = 2;
var firstColumn = 1;
var lastColumn = 3;
var range = sheet.getRange(firstRow, firstColumn, sheet.getLastRow() - firstRow + 1, lastColumn - firstColumn +1); // assuming data appears in A2-C
var rowHeight = range.getHeight();
for (var i = 0; i < rowHeight; i++){
if(!sheet.isRowHiddenByFilter((firstRow+i))){
var row = sheet.getRange((firstRow+i), firstColumn, 1, lastColumn - firstColumn +1);
row.setValues(row.getValues());
}
}
}
This sample copies the non-hidden rows one by one, thus it makes more sense to use the method setValues() instead of copyTo()
I need to pull/import data from "sheet 1" to "sheet 2" based on column 4 being a specific text string. The script should not pull lines that already exist.
I have no idea if this is possible. I can pull the data but it just recopies everything so I have duplicates.
Any help would be super appreciated.
function onEdit() {
var ss = SpreadsheetApp.openById('1Ognzsi6C0DU_ZyDLuct58f5U16sshhBpBoQ8Snk8bhc');
var sheet = ss.getSheetByName('Sheet 1');
var testrange = sheet.getRange('D:D');
var testvalue = (testrange.getValues());
var sss = SpreadsheetApp.getActive();
var csh = sss.getSheetByName('Sheet 1');
var data = [];
var j =[];
for (i=0; i<testvalue.length;i++) {
if ( testvalue[i] == 'Dan') {
data.push.apply(data,sheet.getRange(i+1,1,1,11).getValues());
j.push(i);
}
}
csh.getRange(csh.getLastRow()+1,1,data.length,data[0].length).setValues(data);
}
Sheet 1
Sheet 2
Solution
You should be able to replace your code with this and it will work. You would put this script in the target sheet (Sheet 2), and replace the ID in the first line of the function with the origin (Sheet 1).
I'll leave it up to you to change to an onEdit or to make it a menu item. Right now it can be run from the script editor. onEdit doesn't make sense to me as an appropriate trigger. Maybe you prefer a Time-Driven Trigger. Though a custom menu would be the best way IMO.
function pullData() {
var sourceSs = SpreadsheetApp.openById('[YOUR_SPREADSHEET_ID]');
var sourceRange = sourceSs.getSheetByName('Sheet1').getDataRange();
var sourceHeight = sourceRange.getHeight();
var sourceWidth = sourceRange.getWidth();
var sourceData = sourceSs.getSheetByName('Sheet1').getRange(2, 1, sourceHeight - 1, sourceWidth).getValues();
var targetSs = SpreadsheetApp.getActive();
var targetRange = targetSs.getSheetByName('Sheet1').getDataRange();
var targetHeight = targetRange.getHeight();
var targetWidth = targetRange.getWidth();
var sourceDataChecker = [];
var targetDataChecker = [];
sourceData.forEach((row) => {
sourceDataChecker.push(row[0] + row[1] + row[2] + row[3]);
})
if (targetHeight != 1) {
var targetData = sourceSs.getSheetByName('Sheet1').getRange(2, 1, targetHeight - 1, targetWidth).getValues();
targetData.forEach((row) => {
targetDataChecker.push(row[0] + row[1] + row[2] + row[3]);
});
};
sourceData.forEach((row, i) => {
if (!(targetDataChecker.includes(sourceDataChecker[i]))) {
targetSs.appendRow(row);
};
});
}
Explanation
This script builds an "index" of each row in both sheets by concatenating all the values in the row. I did this because I noticed that sometimes you have "joe" in two rows, and so, you can't simply use column 4 as your index. You are basically checking for any row that is different from one in the target sheet (Sheet 2).
If the target sheet is blank, then all rows are copied.
References
Append Row to end of sheet
Get Data Range (range of sheet that contains data)
Get Range Height (to deal with headers)
Get Range Width
for Each
I have the following problem with this function. It only deletes blank rows below but I want to delete the rows above.
function removeEmptyRows(){
var sh = SpreadsheetApp.getActive();
var sh1=sh.getSheetByName('name');
var range=sh1.getRange('A:A');
var maxRows = sh1.getMaxRows();
var lastRow = sh1.getLastRow();
sh1.deleteRows(lastRow+1, maxRows-lastRow);
}
I tried with the following function
function removeemptyrows(){
var ss=SpreadsheetApp.getActive();
var sh1=ss.getSheetByName('name');
var range=sh1.getRange('A:A');
var values = range.getValues();
for( var i = values.length-1; i >=0; i-- ) {
for( var j = 0; j < values[i].length; j++ )
if( values[i][j] === "" )
sh1.deleteRow(i+1)
}
}
but it deletes rows too slowly - one by one.
You want to delete the rows that the cell of column "A" is empty in the range from 10 row to bottom of sheet.
You want to achieve this using Google Apps Script.
You want to reduce the process cost.
If my understanding is correct, how about this answer? Please think of this as just one of several possible answers.
Modification point:
In this case, I would like to propose to use Sheets API. When Sheets API is used, the rows can be deleted by one API call, even when the rows are discreted.
Sample script:
When you use this script, please enable Sheets API at Advanced Google services.
function removeemptyrows() {
var sheetName = "name"; // Please set the sheet name.
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName(sheetName);
var sheetId = sheet.getSheetId();
var values = sheet.getRange('A10:A').getValues();
var requests = values.reduce(function(ar, [e], i) {
if (!e) ar.push({deleteDimension:{range:{sheetId:sheetId,dimension:"ROWS",startIndex:(i + 9),endIndex:(i + 10)}}});
return ar;
}, []).reverse();
if (requests.length > 0) Sheets.Spreadsheets.batchUpdate({requests: requests}, ss.getId());
}
In this case, the sample script is almost the same with the below script of https://stackoverflow.com/a/60613983/7108653 . As 2 modification parts, in your case, you want to delete the rows of empty cell at the column "A". So 'C6:C' + sheet.getLastRow() and if (e) were modified to 'A10:A' and if (!e), respectively.
References:
Method: spreadsheets.batchUpdate
Advanced Google services
If I misunderstood your question and this was not the direction you want, I apologize.
Every time I make edits to any of the 5 other spreadsheets, as soon as I finish editing a cell, I am returned to sheet name "HOLDING". I would like this to stop happening.
function onChange(e) {
DeleteRow(e);
}
function DeleteRow(e) {
// This Code will delete any "Y" in Column E.
var sheet = SpreadsheetApp.getActive();
sheet.setActiveSheet(sheet.getSheetByName('HOLDING'), true);
var rows = sheet.getDataRange();
var numRows = rows.getNumRows();
var values = rows.getValues();
var rowsDeleted = 0;
for (var i = 0; i <= numRows - 1; i++) {
var row = values[i];
if (row[4] == "Y") {
sheet.deleteRow((parseInt(i)+1) - rowsDeleted);
rowsDeleted++;
}
}
}
Thanks in Advance
The call to setActiveSheet() is what's causing the active sheet to change.
Try replacing the first couple lines of DeleteRow() with an approach that will allow you to interact with the HOLDING sheet without making it active:
function DeleteRow(e) {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('HOLDING');
<...continue with the rest of the original implementation...>
Hi could someone help me with this script im trying to insert a formula in all the blank cell from (J15) to (J38) here is what ive got but I just cant seem to get it the proper way any help would be greatly appreciated.
function test() {
var ssA = SpreadsheetApp.getActive(); //changed from openById() for my convenience
var ss = ssA.getActiveSheet(); //change from getSheetByName() for my convenience
var range = ss.getRange(15,10,24,1); //row 2 column 7 (G) lastRow 1 column
var data = range.getValues(); //Gets all data
for(var i=0;i<data.length;i++) //this runs over entire selected range
{
if(!data[i][0]) //If true then it's blank
{
data[i](('=Iferror(If(G15="",, if($B$5 = Iferror(query(\'Client
List\'!$A$2:$A, "select A where A =\'"&$B$5&"\'"),""),VLOOKUP($B$5,Client_Rate,2,False),VLOOKUP(D15,Config_Rate_List
,2,False))),"")');)
}
}
range.setFormula(data); //Sets all data.
}
You want to put the formula to "J15:J38". If my understanding is correct, how about this modification?
I thought that it is possible that the cells of "J15:J38" have the values and formulas. So in this modified script, the formula is put to the empty cells which don't have both. The flow of script is as follows.
Retrieve values and formulas from "J15:J38".
Create range list.
Put the formula using setFormula().
I think that there are several solutions for your situation. So please think of this as one of them.
Modified script :
function test() {
var ssA = SpreadsheetApp.getActive(); //changed from openById() for my convenience
var ss = ssA.getActiveSheet(); //change from getSheetByName() for my convenience
var range = ss.getRange(15,10,24,1); //row 2 column 7 (G) lastRow 1 column
var data = range.getValues(); //Gets all data
// The following script was modified.
var formulas = range.getFormulas();
var rangeList = [];
var offset = 15;
for(var i=0;i<data.length;i++) {
if(!data[i][0] && !formulas[i][0]) {
rangeList.push("J" + (offset + i));
}
}
var formula = '=Iferror(If(G15="",, if($B$5 = Iferror(query(\'Client List\'!$A$2:$A, "select A where A =\'"&$B$5&"\'"),""),VLOOKUP($B$5,Client_Rate,2,False),VLOOKUP(D15,Config_Rate_List,2,False))),"")';
ss.getRangeList(rangeList).setFormula(formula);
}
Note :
I was not sure whether the sheet name is Client List or ClientList, because of the line break in your script.
I'm not sure about the formula which was put to cells.
About your title, "black" of "Google Script insert formula in 1 column where cell are black" is "blank"?
Reference :
getRangeList(a1Notations)
If this was not what you want, I'm sorry.
Edit :
Unfortunately, the formulas in an array cannot be put the cells using the range list yet. I think that Sheets API can put various formulas once. But as a simple way, here, I would like to propose the following script.
function test() {
var ssA = SpreadsheetApp.getActive(); //changed from openById() for my convenience
var ss = ssA.getActiveSheet(); //change from getSheetByName() for my convenience
var range = ss.getRange(15,10,24,1); //row 2 column 7 (G) lastRow 1 column
var data = range.getValues(); //Gets all data
// The following script was modified.
var formulas = range.getFormulas();
var val = [];
var offset = 15;
for(var i=0;i<data.length;i++) {
if(!data[i][0] && !formulas[i][0]) {
ss.getRange("J" + (offset + i)).setFormula('=Iferror(If(G' + (offset + i) + '="",, if($B$5 = Iferror(query(\'Client List\'!$A$2:$A, "select A where A =\'"&$B$5&"\'"),""),VLOOKUP($B$5,Client_Rate,2,False),VLOOKUP(D' + (offset + i) + ',Config_Rate_List,2,False))),"")');
}
}
}