Remove empty rows in Google Sheets - google-apps-script

​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.

Related

How to get filtered values from Filter after using setColumnFilterCriteria?

I m in trouble using some filter in appscript.
I can see that the spreasheet is filtering, but programaticaly i don't see any changes.
Could you help ?
Thanks
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("xxxx");
var values = sheet.getDataRange().getValues();
Logger.log("VALUES "+values.length);
var newCriteria = SpreadsheetApp.newFilterCriteria().whenTextEqualTo('51').build();
var range = sheet.getFilter().setColumnFilterCriteria(22, newCriteria).getRange(); //The 1-indexed position of the column.
values = range.getValues();
Logger.log("VALUES "+values.length);
Results on logging :
19-08-28 19:27:33:272 CEST] VALUES 1379
[19-08-28 19:27:39:748 CEST] VALUES 1379
You want to retrieve values from the filtered sheet in the Spreadsheet.
You want to achieve this using Google Apps Script.
If my understanding is correct, how about this answer?
Issue and workaround:
Unfortunately, the values cannot be retrieved from the filtered sheet using getValues(). This has already been mentioned by TheMaster`s comment. As the workarounds, I would like to propose the following 2 patterns.
Pattern 1:
When the Spreadsheet is published, the filtered sheet can be seen. In this pattern, this is used, and the values are retrieved using the query Language. But don't worry. In this script, the access token is used. So the filtered sheet can be directly retrieved without publishing Spreadsheet. I think that this is the simple way.
Modified script:
var ss = SpreadsheetApp.getActiveSpreadsheet(); // Added
var sheet = ss.getSheetByName("xxxx"); // Modified
var values = sheet.getDataRange().getValues();
Logger.log("VALUES "+values.length);
var newCriteria = SpreadsheetApp.newFilterCriteria().whenTextEqualTo('51').build();
var range = sheet.getFilter().setColumnFilterCriteria(22, newCriteria).getRange(); //The 1-indexed position of the column.
// values = range.getValues();
// I added below script.
var url = "https://docs.google.com/spreadsheets/d/" + ss.getId() + "/gviz/tq?tqx=out:csv&gid=" + sheet.getSheetId() + "&access_token=" + ScriptApp.getOAuthToken();
var res = UrlFetchApp.fetch(url);
var values = Utilities.parseCsv(res.getContentText());
Logger.log("VALUES "+values.length);
Pattern 2:
In this pattern, Sheets API is used. Before you use this script, please enable Sheets API at Advanced Google services.
Modified script:
var ss = SpreadsheetApp.getActiveSpreadsheet(); // Added
var sheet = ss.getSheetByName("xxxx"); // Modified
var values = sheet.getDataRange().getValues();
Logger.log("VALUES "+values.length);
var newCriteria = SpreadsheetApp.newFilterCriteria().whenTextEqualTo('51').build();
var range = sheet.getFilter().setColumnFilterCriteria(22, newCriteria).getRange(); //The 1-indexed position of the column.
// values = range.getValues();
// I added below script.
var res = Sheets.Spreadsheets.get(ss.getId(), {
ranges: ["xxxx"], // <--- Please set the sheet name.
fields: "sheets/data"
});
var values = res.sheets[0].data[0].rowMetadata.reduce(function(ar, e, i) {
if (!e.hiddenByFilter && res.sheets[0].data[0].rowData[i]) {
ar.push(
res.sheets[0].data[0].rowData[i].values.map(function(col) {
return col.userEnteredValue[Object.keys(col.userEnteredValue)[0]];
})
);
}
return ar;
}, []);
Logger.log("VALUES "+values.length);
References:
Query Language
Method: spreadsheets.get
Advanced Google services
If I misunderstood your question and this was not the result you want, I apologize.

Google Sheet App Script - How to only setValues only unique values

In this spreadsheet: https://docs.google.com/spreadsheets/d/1givNbMvgzD8lbk6NAcwjkpp4-A_D8MetltHjEpinOAI/edit#gid=0
I'd like to Combine only unique Links and Category into Combined sheet.
Right now, with my script, it can only combine all existing data:
function combine() {
var ss = SpreadsheetApp.getActive();
var allsheets = ss.getSheets();
var sourceID = '1givNbMvgzD8lbk6NAcwjkpp4-A_D8MetltHjEpinOAI';
var targetID = '1givNbMvgzD8lbk6NAcwjkpp4-A_D8MetltHjEpinOAI';
var sheetExclude = ["Combined"];
var sheetExcludeIndex = new Array(sheetExclude.length);
for (var s in allsheets) {
var sheet = allsheets[s];
for (var e in sheetExclude) {
if (String(sheet.getName() == sheetExclude[e])) {
sheetExcludeIndex[e] = sheet.getIndex;
}
}
}
allsheets.splice(sheetExcludeIndex, sheetExclude.length);
for (var s in allsheets) {
var sheet = allsheets[s];
updateSourceToTarget(sourceID, sheet.getName(), targetID, 'Combined');
}
}
function updateSourceToTarget(sourceID, sourceName, targetID, targetname) {
Logger.log(sourceID + ' ' + sourceName + ' ' +targetname);
var source = SpreadsheetApp.openById(sourceID).getSheetByName(sourceName);
var destination = SpreadsheetApp.openById(targetID).getSheetByName(targetname);
var sourcelastRow = source.getLastRow();
var sourcelastCol = source.getLastColumn();
var destinationlastRow = destination.getLastRow();
var destinationlastCol = destination.getLastColumn();
var sourcedata = source.getRange(2, 9, sourcelastRow, 10).getValues();
destination.getRange(destinationlastRow + 1, 2, sourcelastRow, sourcelastCol).setValues(sourcedata);
}
However, I'd like to only combine unique links from Sheet2 and Sheet3:
In red is unique data
Sheet2:
Sheet3:
How can I efficiently add only unique values to Combined from Sheet2& Sheet3?
You want to put the values, which removed the duplicated links, to the target sheet (in this case, it's Combined sheet.).
The duplicated links are checked from the target sheet and source sheets. In this case, the target sheet and source sheets are Combined, Sheet2 and Sheet3, respectively.
In your sample Spreadsheet, you want to put the following rows to the target sheet.
https://thehill.com/policy/national-security/department-of-homeland-security/460158-new-hampshire-border-patrol BorderSecurity
https://abcnews.go.com/International/climate-change-frontier-worlds-northernmost-town/story?id=65381362 ClimateChange
You want to achieve this by modifying your Google Apps Script.
If my understanding is correct, how about this modification? Please think of this as just one of several answers.
Modified script:
Please modify your script as follows. In this modification, your function of updateSourceToTarget() is not used.
From:
for (var s in allsheets) {
var sheet = allsheets[s];
updateSourceToTarget(sourceID, sheet.getName(), targetID, 'Combined');
}
To:
// Retrieve values from the target sheet.
var targetSheet = ss.getSheetByName(sheetExclude[0]);
var targetValues = targetSheet.getRange("B2:C" + targetSheet.getLastRow()).getValues();
// Retrieve values from all source sheets. <--- Modified
var sourceValues = allsheets.reduce(function(ar, sheet) {
var v = sheet.getRange(2, 9, sheet.getLastRow() - 1, 10).getValues().filter(function(r) {return r[0] && r[1]});
if (v.length > 0) {
v = v.filter(function(e) {return !ar.some(function(f) {return e[0] === f[0]})});
Array.prototype.push.apply(ar, v);
}
return ar;
}, []);
// Remove the duplication values between the target sheet and all source sheets.
var dstValues = sourceValues.filter(function(e) {return !targetValues.some(function(f) {return e[0] === f[0]})});
// Add the result values to the target sheet.
if (dstValues.length > 0) {
var destination = SpreadsheetApp.openById(targetID).getSheetByName(sheetExclude[0]);
destination.getRange(destination.getLastRow() + 1, 2, dstValues.length, dstValues[0].length).setValues(dstValues);
}
The flow of this modified script is as follows.
Retrieve values from the target sheet.
Retrieve values from all source sheets.
Remove the duplication values between the target sheet and all source sheets.
Add the result values to the target sheet.
Note:
When your shared Spreadsheet is used as the target (Combined) and source sheets (Sheet2 and Sheet3), the following rows are added to the target sheet.
https://thehill.com/policy/national-security/department-of-homeland-security/460158-new-hampshire-border-patrol BorderSecurity
https://abcnews.go.com/International/climate-change-frontier-worlds-northernmost-town/story?id=65381362 ClimateChange
References:
reduce()
filter()
some()
If I misunderstood your question and this was not the direction you want, I apologize.
Added:
In this additional script, a hash table is used for this situation, as mentioned by TheMaster's comment. For example, a sample can be also seen at this thread. In your situation, at first, all values are retrieved from all sheets including Combined sheet, and the hash table is created. By this, the duplicated values are removed. Then, the converted values to an array are put to the Spreadsheet.
Sample script:
Please modify your script as follows.
From:
allsheets.splice(sheetExcludeIndex, sheetExclude.length);
for (var s in allsheets) {
var sheet = allsheets[s];
updateSourceToTarget(sourceID, sheet.getName(), targetID, 'Combined');
}
To:
// allsheets.splice(sheetExcludeIndex, sheetExclude.length); // In this script, this line is not used.
// Retrieve values from the target sheet.
var targetSheet = ss.getSheetByName(sheetExclude[0]);
var targetValues = targetSheet.getRange("B2:C" + targetSheet.getLastRow()).getValues();
// Retrieve values from all source sheets.
// Remove the duplication values between the target sheet and all source sheets.
var sourceValues = allsheets.reduce(function(obj, sheet) {
var v = sheet.getRange(2, 9, sheet.getLastRow() - 1, 10).getValues().filter(function(r) {return r[0] && r[1]});
if (v.length > 0) v.forEach(function(e) {if (!(e[0] in obj)) obj[e[0]] = e[1]});
return obj;
}, {});
var dstValues = Object.keys(sourceValues).map(function(e) {return [e, sourceValues[e]]});
// Add the result values to the target sheet.
if (dstValues.length > 0) {
var destination = SpreadsheetApp.openById(targetID).getSheetByName(sheetExclude[0]);
destination.getRange(2, 2, destination.getLastRow(), 2).clearContent();
destination.getRange(2, 2, dstValues.length, dstValues[0].length).setValues(dstValues);
}
The proposed 2 sample scripts can be used for your situation. So please select one of them.

More Efficient Script for Delete Row If Cell Contains "DEL"

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();
}
}

Google Apps Script Find bottom of column

I need some assistance with Google Apps Script. I am working in google sheets and currently have the following script:
function transpose() {
var spreadsheet = SpreadsheetApp.getActive();
spreadsheet.getRange('C3:N3').activate();
spreadsheet.setActiveSheet(spreadsheet.getSheetByName('Sheet21'), true);
spreadsheet.getRange("'A/P'!C3:N3").copyTo(spreadsheet.getActiveRange(),
SpreadsheetApp.CopyPasteType.PASTE_NORMAL, true);
};
Rather than just paste in A1 of Sheet 21, I would like it to find the bottom of column F. I am also wondering how to copy data from whichever sheet I am in, not just the sheet named A/P.
Thank you in advance!
You want to add the values of C3:N3 in the active sheet to the next row of last row on column F in Sheet21. If my understanding is correct, how about this modification?
Modification points :
In your script, if the active sheet is not Sheet21, the active range becomes "A1", because spreadsheet.getRange('C3:N3').activate() at other sheet is changed by spreadsheet.setActiveSheet(spreadsheet.getSheetByName('Sheet21'), true). By this, the values of "'A/P'!C3:N3" are copied for "A1" of Sheet21.
Flow of modified script:
Retrieve the range of source (Active sheet)
Retrieve the destination (Sheet21) sheet.
Retrieve the destination range.
Copy
Modified script : Pattern 1
If the address of last row of column "F" in "Sheet21" is smaller than that of other columns, please use this.
function transpose() {
var spreadsheet = SpreadsheetApp.getActive();
var srcRange = spreadsheet.getActiveSheet().getRange('C3:N3');
var dstSheet = spreadsheet.getSheetByName('Sheet21');
var range = dstSheet.getRange('F1:F');
var values = range.getValues();
var formulas = range.getFormulas();
var i;
for (i = values.length - 1; i >= 0; i--) {
if (values[i][0] != "" || formulas[i][0] != "") break;
}
var dstRange = dstSheet.getRange("F" + (i + 2));
srcRange.copyTo(dstRange, SpreadsheetApp.CopyPasteType.PASTE_NORMAL, true);
};
Modified script : Pattern 2
If the address of last row of column "F" in "Sheet21" is larger or the same with that of other columns, please use this.
function transpose() {
var spreadsheet = SpreadsheetApp.getActive();
var srcRange = spreadsheet.getActiveSheet().getRange('C3:N3');
var dstSheet = spreadsheet.getSheetByName('Sheet21');
var dstRange = dstSheet.getRange("F" + (dstSheet.getLastRow() + 1));
srcRange.copyTo(dstRange, SpreadsheetApp.CopyPasteType.PASTE_NORMAL, true);
};
If I misunderstand your question, please tell me. I would like to modify it.

Google Script insert formula in 1 column where cell are black

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))),"")');
}
}
}