Google Scripts delete duplicates from select range (not whole sheet) - google-apps-script

Below is a Google Sheets script for deleting duplicate rows from a spreadsheet. While it does work in removing duplicate rows it also destroys in-cell formulas in the process. I currently have a series of sheets where all of the raw data is contained within Columns A:P and all of my formulas are relegated to Columns Q:T.
In my attempts to limit the following script to work only on Columns A:P I receive a missing argument error upon running the script.
var sheet = SpreadsheetApp.getActiveSheet();
var data = sheet.getDataRange("A2:P").getValues();
TL:DR, I love the script, but I want to limit the range upon which it is ran. Any help? Thank you.
https://developers.google.com/apps-script/articles/removing_duplicates
function removeDuplicates() {
var sheet = SpreadsheetApp.getActiveSheet();
var data = sheet.getDataRange().getValues();
var newData = new Array();
for(i in data){
var row = data[i];
var duplicate = false;
for(j in newData){
if(row.join() == newData[j].join()){
duplicate = true;
}
}
if(!duplicate){
newData.push(row);
}
}
sheet.clearContents();
sheet.getRange(1, 1, newData.length,
newData[0].length).setValues(newData);
}
EDIT: I believe the following is above a valid question, but I would like to add a new observation that may have some impact on what's happening(??). When I initiate a find and replace from within sheets to remove all instances of the word 'null' then sheets removes null and leaves every other cell alone. However if I run a script to remove 'null' it reformats all of my cells changing dates & times to decimals. etc. Is there a means by which to run a script and avoid overall unintended actions such as sheet wide reformatting?

You will need to use getRange() function to specify the range on which you want to work with.
var sheet = SpreadsheetApp.getActiveSheet();
var rng = sheet.getRange("A2:P")
var data = rng.getValues();
Also, you clear the entire sheet with this line sheet.clearContents(); instead use this
rng.clearContents()
That way you only clear contents in the range specified by rng
Your final code should look like this
function removeDuplicates() {
var sheet = SpreadsheetApp.getActiveSheet();
var rng = sheet.getRange("A2:P")
var data = rng.getValues();
var newData = new Array();
for(i in data){
var row = data[i];
var duplicate = false;
for(j in newData){
if(row.join() == newData[j].join()){
duplicate = true;
}
}
if(!duplicate){
newData.push(row);
}
}
rng.clearContents();
sheet.getRange(1, 1, newData.length,
newData[0].length).setValues(newData);
}

It's been a few years since the answer was provided. For some reason this wasn't working for me now.
I removed the "s," making rng.clearContents(); into rng.clearContent();
Also the range start from row 1 to row 2 making sheet.getRange(1, 1, newData.length, newData[0].length).setValues(newData); into sheet.getRange(2, 1, newData.length, newData[0].length).setValues(newData);
Worked for me. Thank you!

Related

How to copy a specific row to a specific row in one sheet? (Google apps script)

I want to create a script that takes a certain row and copies it to a certain row.
I have already found many solutions for this. The problem is that these solutions always look at the last row of a whole sheet. I only want the last row from column. Can anyone help me? Many thanks in advance.
script :
function daily_transfer_stockcars() {
var sp = SpreadsheetApp.getActiveSpreadsheet(); //GDoc
var sourcess = sp.getSheetByName("Tabellenblatt1"); //Source_Sheet
var source_range = sourcess.getRange("J5:N5"); //Source_Data_Range
var targetss = sp.getSheetByName("Tabellenblatt1");
source_range.copyTo(targetss.getRange("J" + (getLastDataRow(targetss,"J")+1)), {contentsOnly: true});
}
function getLastDataRow(sheet,col) {
// col en letter
var lastRow = sheet.getLastRow();
var range = sheet.getRange(col + lastRow);
if (range.getValue() !== "") {
return lastRow;
} else {
return range.getNextDataCell(SpreadsheetApp.Direction.UP).getRow();
}
}

Sheet Script to Dedupe Rows: Unexpected Behavior

I have a script which posts data from one sheet to another and then de-dupes the data. The dedupe script appears to only dedupe SOME of the data. For instance, rows 2-13 remain duplicates while rows after row 35 are deduped. Rows at the top are posted from the insert script.
How can I get the script to:
a) Dedupe the full range of data.
b) Remove the new duplicate rows posted from the insert script rather than rows that were already there.
I've tried setting the full range of the sheet to be checked, but this seems to interfere with the post to the new data. This is the simplest script I've managed to find/develop.
function Run(){
insert();
dedupe();
}
function insert() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var source = ss.getSheetByName('Candidate Refresh'); // change here
var des = ss.getSheetByName('Candidate Listing'); // change here
var sv = source
.getDataRange()
.getValues();
sv.shift();
des.insertRowsAfter(1, sv.length);
des.getRange(2, 1, sv.length, source.getLastColumn()).setValues(sv);
}
function dedupe() {
var sheet = SpreadsheetApp.getActive().getSheetByName('Candidate Listing');
var data = sheet.getDataRange().getValues();
var newData = new Array();
for(i in data){
var row = data[i];
var duplicate = false;
for(j in newData){
if(row.join() == newData[j].join()){
duplicate = true;
}
}
if(!duplicate){
newData.push(row);
}
}
sheet.clearContents();
sheet.getRange(1, 1, newData.length,
newData[0].length).setValues(newData);
}
I would expect new data to be posted, the full range of data to be checked for duplicates, and new data posted by the script to be removed if it is found to be a duplicate.
So I think my original post did work from the de-dupe perspective but I also needed it to only look at one value so I found the below script to do so which works much better.
function removeDuplicates() {
var sheet = SpreadsheetApp.getActiveSheet();
var rows = sheet.getLastRow();
var firstColumn = sheet.getRange(1, 2, rows, 1).getValues();
firstColumn = firstColumn.map(function (e) {return e[0]})
for (var i = rows; i >0; i--) {
if (firstColumn.indexOf(firstColumn[i-1]) != i-1) {
sheet.deleteRow(i);
}
}
}

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 App-script Autosort on edit

Ive been working on automatically sorting my data (ascending based on 2nd row 1st column data) and I found some tips through searching online but encountered an error which seems I cant find an answer through the net.
so heres the scenario:
I have 2 sheets, Sheet1 & Sheet2, the data from sheet1 is linked through sheet2 although sheet2 has additional columns,
this is sheet1
and this is sheet2
notice that the Column lastname and code in both sheets are thesame, the difference is the column Gender (Formatted on drop-down list) & Bdate(cell formatted as date)
I found a script that seems to work but I does not properly work completely, here is the output after I run the script.
notice the columns that inside the red box, it seems gender and bdate didnt follow the auto sort.
here is my code:
function autosortOnEdit() {
var sheetNames = ["Sheet1", "Sheet2"];
var ss = SpreadsheetApp.getActiveSpreadsheet();
sheetNames.forEach(function(name) {
var sheet = ss.getSheetByName(name);
var range = sheet.getRange(2, 1, sheet.getLastRow() - 1, sheet.getLastColumn() -1);
range.sort({column: 1, ascending: true});
});
}
my observation is I think this script does not work on cells that are formatted like the example above.
I want to sort this automatically based on column A "last name".
how can i make this script work even on formatted cells?
Thanks in Advance, I will continue searching through the net.
Not sure how to use range.sort({column: 1, ascending: true}); or how does it work, but whenever I want to sort sheet values, I do the following:
function myFunction()
{
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var rows = sheet.getLastRow();
var columns = sheet.getLastColumn();
var sortedValues = sheet.getRange(2, 1, rows-1, columns).getValues().sort();
sheet.getRange(2, 1, rows-1, columns).setValues(sortedValues);
}
Try this instead of your code, hope this helps as it is successfully sorting all the values when I tested.
EDIT
To apply the same to all sheets inside a spreadsheet, you can get sheet names and iterate it in for loop one by one. Check the code:
function myFunction()
{
var sheets = SpreadsheetApp.getActiveSpreadsheet().getSheets();
var currentSheetName, currentSheet;
for(var i=0; i<sheets.length; i++)
{
currentSheetName = sheets[i].getName();
currentSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(currentSheetName);
var rows = currentSheet.getLastRow();
var columns = currentSheet.getLastColumn();
var sortedValues = currentSheet.getRange(2, 1, rows-1, columns).getValues().sort();
currentSheet.getRange(2, 1, rows-1, columns).setValues(sortedValues);
}
}

How to add a button per row in google spreadsheet?

I have a simple spreadsheet with multiple rows I want to add button to each of it automatically.
On click this button will remove the entire row and move it to another spreadsheet.
I've looked online and couldn't find a way to add buttons to spreadsheets. Some people suggest to insert drawings and assign macros, but looks like the latest version lacks this feature.
I've also read about google app sript. I was able to add buttons to a menu, but not directly to each row of a spreadsheet.
Do you have any advice or suggestion on how to better achieve this?
I suggest the you create a column(say 10) at the end of each row with a List Data Validation of "Move Row" and create an onEdit function like so
function onEdit(eventInfo) {
if (eventInfo.range.getColumn() == 10 && eventInfo.value == "Move Row")
{
var ss = eventInfo.source;
var row = eventInfo.range.getRow();
var range = ss.getRange(row, 1, 10) // this is your range, do with it what you will
}
}
So this script is not entirely my own work, but the results of something I was working on with help.
It will look in a source sheet, look at a column you specify (by number), and look for a value in this sheet.
If matched, it will move that row to another sheet, within the spreadshet, of your choosing.
function moveRowInternalSheet()
{
var sourceSheet = "";
var columnNumberToWatch = ;
var valueToFind = "";
var destinationSheet = "";
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName(sourceSheet);
var values = sheet.getDataRange().getValues();
var counter = 1; //starts looking in second row, due to header
var archive = [];
while (counter < values.length)
{
if (values[counter][columnNumberToWatch - 1] == valueToFind)
{
archive.push(values.splice(counter, 1)[0]);
}
else
{
// If the value to find is not found. Then increment the counter by 1
counter++;
}
}
var archiveLength = archive.length;
if (!archiveLength) return;
var targetSheet = ss.getSheetByName(destinationSheet);
var lastRow = targetSheet.getLastRow();
var requiredRows = lastRow + archiveLength - targetSheet.getMaxRows();
if (requiredRows > 0) targetSheet.insertRowsAfter(lastRow, requiredRows);
targetSheet.getRange(lastRow + 1, 1, archiveLength, archive[0].length).setValues(archive);
sheet.clearContents();
sheet.getRange(1, 1, values.length, values[0].length).setValues(values);
}