Delete Duplicate Row in Google Spreadsheet - google-apps-script

I need this script to delete the row (within the Registration sheet) with the matching Registration Code to the Cancel Registration sheet's Registration code. As of now, this script only deletes a row if "sheetR.deleteRow(i);" is not inside "if (regCodeR === regCodeCR) {}". It doesn't delete the correct row either.
function rD() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheetR = ss.getSheetByName("Registration");
var sheetCR = ss.getSheetByName("Cancel Registration")
var dataR = sheetR.getDataRange().getValues();
var dataCR = sheetCR.getDataRange().getValues();
var headerRow = 1;
for (var i = 1; i in dataR && i in dataCR; ++i) {
var rowR = dataR[i];
var rowCR = dataCR[i];
var duplicate = false;
var regCodeR = sheetR.getRange(headerRow + i, 10).getValues();
var regCodeCR = sheetCR.getRange(headerRow + i, 9).getValues();
if (rowR[9] === rowCR[8]) {
duplicate = true;
}
}
if (regCodeR === regCodeCR) {
sheetR.deleteRow(i);
}
}

I tried this code with simple data having one column with registration numbers. You can modify the main sheet range according to your data. Also make sure to change the sheet names.
Tried and tested the code below :
function deleteDup(){
var mainSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('regis');
var cancelSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('cancel');
var mainSheetValues = mainSheet.getRange(2, 1, mainSheet.getLastRow(),1).getValues();
var cancelSheetValues = cancelSheet.getRange(2, 1, cancelSheet.getLastRow(),1).getValues();
var k = 0;
var del = [];
// Getting the row index of matching values
for (i = 0; i < cancelSheetValues.length; i++)
{
var cancelValue = cancelSheetValues[i][0];
Logger.log(cancelValue);
for (j = 0; j < mainSheetValues.length; j++)
{
if(mainSheetValues[j][0] == cancelValue){
del[k] = j;
k++;
}
}
}
del.sort();
var count =0;
// deleting the values from the main sheet values array
for (i = 0; i < k; i++ )
{
mainSheetValues.splice((del[i] - count), 1);
count++;
}
var len = mainSheetValues.length;
// Update the sheet with new values
mainSheet.getRange(2, 1, mainSheet.getLastRow(),1).clearContent();
mainSheet.getRange(2, 1, len,1).setValues(mainSheetValues);
}
But instead you can also do this way:
Set a flag value in the registration sheet if it matches with the cancellation sheet.
Then loop through the registration sheet data and delete the matching flag row.
Hope that helps!

Related

How to loop through cells in Google Sheets and log information if specific variables exist

You will need this link to solve this: - https://docs.google.com/spreadsheets/d/11PjVSWPfqOBPSej3AlQ-_T8Bws66kevR08Q2NRTmVcE/edit?usp=sharing
So this is a tricky one. I am looking to loop through the name in Sheet1 Column A, and also loop through the type in Column C. Then if Column A in Sheet1 matches the name of column A in Sheet2, AND the type is "pickup" I want to log the other information that is in Sheet 1 inside of Column C on Sheet 2. Log the things like "State", "Location", "City" next to the identical name in Sheet 2. I hope that makes sense.
I know I probably need to use a loop within a loop to do all this, but this is as far as I can get as I cannot figure out how to write the loop and then log the rows where the 2 names match.
function myFunction() {
function pullDeliveryNotes()
{
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var sheet2 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet2");
var dataRange = sheet.getDataRange();
var dataRange2 = sheet2.getDataRange();
var rng = sheet.getRange(2,1, dataRange.getLastRow()-1,dataRange.getLastColumn());
var rng2 = sheet.getRange(2,3, dataRange.getLastRow()-1,dataRange.getLastColumn());
var rng3 = sheet2.getRange(2,1, dataRange2.getLastRow()-1,dataRange2.getLastColumn());
var rngA = rng.getValues().toString()
var rngB = rng2.getValues().toString()
var rngC = rng3.getValues().toString()
for(var i = 0; i < rng.length; i++) {
for (var x = 0; x < rng2.length; x++) {
for (var y = 0; y < rng3.length; y++) {
if (rng[i][x][y].includes(rng3[i][x][y]) && rng2[i][x][y] === "pickUp") {
Logger.log("We got it")
}
}
}
}
}
}
Based on your problem statement.
Try this sample script:-
function checkValues() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const ssSource = ss.getSheetByName('Sheet1')
const ssTarget = ss.getSheetByName('Sheet2')
const sourceRange = ssSource.getDataRange().getValues().filter(r=> r[2] === 'Pickup'); // Filtering only pickup values
const targetRange = ssTarget.getDataRange().getValues();
for(var i = 0 ; i < sourceRange.length ; i++)
{
for(var j = 1 ; j < targetRange.length ; j++)
{
if(sourceRange[i][0] === targetRange[j][0]) // if name matches
{
ssTarget.getRange(`C${j+1}`).setValue(`${sourceRange[i][3]},${sourceRange[i][4]},${sourceRange[i][5]}`) // set values in column C
}
}
}
}

Reduce script execution time - Google script

I made a script that works properly (does what I want it to), however, it's painfully slow and at this pace, it will finish in about 20 days. I can't wait for 20 days and I'm not good enough at this to make it faster on my own.
Here's a brief description of the task:
Masterlist - it's a sheet with 23 columns and 29000+ rows.
Seed - it's an empty sheet that I'm to copy the Masterlist to.
Duplicates - it's an empty sheet where I will store any duplicate rows.
The process:
Get the first line from Masterlist. Check if line already in Seed. If line not in Seed, add line. If line already in Seed, add line to Duplicates. Either way, delete the original line from the Masterlist.
The definition of duplicate:
Each line has an emails column. Column can be either a single email address, or multiple email addresses separated by "; ". If an email is found within line in Masterlist and already exists within line in Seed, this whole line is considered a duplicate.
Example:
"aaa#gmail.com" is not a duplicate of "a#gmail.com; aa#gmail.com"
"bbb#gmail.com" is a duplicate of "b#gmail.com; bbb#gmail.com"
Furthermore, if the emails cell is empty in the Masterlist, this is not considered a duplicate.
Here comes my code - it works but is not fast enough.
function getSheet(name){
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(name);
return sheet;
}
function getRowByID(sheet, rowID) {
var range = sheet.getRange(rowID, 1, 1, 23);
var value = range.getValues();
return [range, value];
}
//main executes the entire thing
function main(){
var sourceSheet = getSheet('Masterlist');
var targetSheet = getSheet('Seed');
var remainingSheet = getSheet('Duplicates');
var counter = sourceSheet.getLastRow();
var start = new Date();
while(counter >= 2){
var sourceLine = getRowByID(sourceSheet, 2)[1];
var duplicates = checkEmailMatch(sourceLine, targetSheet);
if(duplicates == 0){
targetSheet.appendRow(sourceLine[0]);
sourceSheet.deleteRow(2);
}
else{
remainingSheet.appendRow(sourceLine[0]);
sourceSheet.deleteRow(2);
}
counter--;
}
}
//iterates through existing lines in the Seed sheet (locates the email cell and reads its contents)
function checkEmailMatch(row, seed){
var sourceEmail = row[0][7];
var counter = seed.getLastRow();
var result = [];
if(!counter){
return 0;
}
else{
var j = 0;
var i = 2;
for(i; i <= counter; i++){
var seedLine = getRowByID(seed, i)[1];
var seedEmail = seedLine[0][7];
if(!seedEmail){}
else if(compareEmails(seedEmail, sourceEmail) == true) {
result[j] = i;
j++;
}
}
return result;
}
}
//Compares each email in Masterlist ("; " separated) with each email in Source ("; " separated)
function compareEmails(emailSeedCell, emailSourceCell){
var seedEmails = emailSeedCell.split("; ");
var sourceEmails = emailSourceCell.split("; ");
for(var i = 0; i < seedEmails.length; i++){
for(var j = 0; j < sourceEmails.length; j++){
if(seedEmails[i] == sourceEmails[j]) return true;
}
}
return false;
}
Please help me - if you need any additional info, I'd be happy to provide! Please note that this is my third script ever, so any feedback is welcome!
Thanks to everyone who chipped in to help, I managed to come up with this code that reduced the execution time more than 10000 times! Thanks, everyone - here's the code:
function sheetToArray(name){
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(name);
var counter = sheet.getLastRow();
var columns = sheet.getLastColumn();
var array = sheet.getRange(2, 1, counter, columns).getValues();
return array;
}
function compareEmails(emailSeedCell, emailSourceCell){
var seedEmails = emailSeedCell.split("; ");
var sourceEmails = emailSourceCell.split("; ");
var result = false;
for(var i = 0; i < seedEmails.length; i++){
for(var j = 0; j < sourceEmails.length; j++){
if(seedEmails[i] == sourceEmails[j]) result = true;
}
}
return result;
}
function save2DArrayToSpreadsheet(name, array){
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(name);
sheet.getRange(2, 1, array.length, array[0].length).setValues(array);
}
function main(){
var masterArray = sheetToArray('Masterlist');
var seedArray = [];
var duplicateArray = [];
for(var i = 0; i < masterArray.length; i++){
Logger.log(i);
if(!seedArray.length){
seedArray.push(masterArray[i]);
}
else if(!masterArray[i][7]){
seedArray.push(masterArray[i]);
}
else{
var result = false;
for(var j = 0; j < seedArray.length; j++){
if(compareEmails(seedArray[j][7], masterArray[i][7]) == true){
result = true;
}
}
if(result == true){
duplicateArray.push(masterArray[i]);
}
else{
seedArray.push(masterArray[i]);
}
}
}
save2DArrayToSpreadsheet("Seed", seedArray);
save2DArrayToSpreadsheet("Duplicates", duplicateArray);
}

Find all empty cells in several columns

Is there an easy way to color the rows containing empty cells in specific columns? So far, I only came up with a solution to highlight the cells themselves, but this script also takes some time to run.
Will appreciate any advice or guidance!
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('FRs & NFRs')
var columns = ['J17:J','L17:L', 'V17:V', 'AB17:AB','AI17:AI', 'AK17:AK'];
for(var col in columns){ //loop to iterate the desired ranges
var range = sheet.getRange(columns[col]);
//range.activate();
var values = range.getValues();
var formulas = range.getFormulas();
//for each row that data is present
for(var i = 0; i < values.length; i++) { //loop to iterate the rows
for( var j = 0; j< values[i].length ; j++){ //loop to iterate the columns
var cell = range.getCell(i+1, j+1);
if ( values[i][j] == "" )
{
cell.setBackground('red');
cell.activate
}
else
{ cell.setBackground('white')
}
Use ConditionalFormatRuleBuilder instead. This will create a Conditional format rule in your Sheet.
Try this.
Code:
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('FRs & NFRs');
var ranges = sheet.getRangeList(['J17:J','L17:L', 'V17:V', 'AB17:AB','AI17:AI', 'AK17:AK']);
var rule = SpreadsheetApp.newConditionalFormatRule()
.whenCellEmpty()
.setBackground('red')
.setRanges(ranges.getRanges())
.build();
var sheetRules = sheet.getConditionalFormatRules();
sheetRules.push(rule);
sheet.setConditionalFormatRules(sheetRules);
}
Output:
Conditional Formatting:
Highlighting empty cells using rangeList.setBackgroundColor(color) ​
In my code below, I collected the cell name of the empty cells and use it to create a RangeList. Then I used the RangeList to set the background to red. This reduced the Google Sheet Service calls from thousands to 11.
function columnToLetter(column)
{
var temp, letter = '';
while (column > 0)
{
temp = (column - 1) % 26;
letter = String.fromCharCode(temp + 65) + letter;
column = (column - temp - 1) / 26;
}
return letter;
}
function highlightEmptyCell(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('FRs & NFRs')
var rangeList = sheet.getRangeList(['J17:J','L17:L', 'V17:V', 'AB17:AB','AI17:AI', 'AK17:AK']);
var rangeArray = rangeList.getRanges();
var ranges = [];
rangeArray.forEach(range =>{
var data = range.getValues();
for( var i = 0; i < data.length; i++ ){
for( var j = 0; j < data[0].length; j++ ){
if(data[i][j] == ""){
var cellName = columnToLetter(range.getColumn()) + (i + 17)
ranges.push(cellName);
}
}
}
});
sheet.getRangeList(ranges).setBackground("red");
}
Reference:
Class ConditionalFormatRuleBuilder
RangeList.setBackgroundColor(color)

How can I transponse a column of data into rows of data, using empty cells as indicators to start a new row?

To start out, here is my sheet that gives two examples of sets of data and the desired results for each.
https://docs.google.com/spreadsheets/d/1MPppt2yztfPtz2iSssSfIuBoYccxQ4gs7PZdygdz1Z8/edit?usp=sharing
Here is the code that I have right now. It is not in a working state, but it seems to be close. Will edit as I experiment different solutions.
// on open, add menu and bttons to test functions
function onOpen() {
var spreadsheet = SpreadsheetApp.getActive();
var menuItems = [
{name: 'Analyze and flip data selection', functionName: 'flipSelection'},
{name: 'Check for first empty cell', functionName: 'firstEmptyCell'}
];
spreadsheet.addMenu('Scripts', menuItems);
}
// put data from a column into a row. on a blank line, write to new row
function flipSelection() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var testSheet = ss.getSheetByName("test");
var sheet = ss.getSheetByName("main");
var range = sheet.getActiveRange();
var numRows = range.getNumRows();
var numCols = range.getNumColumns();
var data = [];
// check active range(current selection) for values and store them in data array
for (var i = 1; i < numRows; i++){
for (var j = 1; j < numCols; j++) {
var currentValue = range.getCell(i,j).getValue();
data.push(currentValue);
}
}
// log array to verify accurate data
for (var k = 0; k < data.length; k++){
ss.toast(data[k].toString());
Logger.log(data[k].toString());
}
// for each string in data array, write to next cell. if the string is blank, go to next row and write to next cell (recursion??)
for (var n = 0; n < data.length; n++) {
// Do we have data? If not, move on
if (data[n] == "") {
//target next empty row (need help here!)
continue;
}
const colA = testSheet.getRange('A:A').getValues().join().split(",");
const rowIndex = colA.indexOf('');
const colIndex = firstEmptyCell(rowIndex); // assuming firstEmptyCell works correctly
const targetCell = testSheet.getRange(rowIndex, colIndex);
targetCell.setValue(data[n]);
}
}
// a way to find first empty cell
function firstEmptyCell (emptyRow) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var testSheet = ss.getSheetByName("test");
var range = testSheet.getRange(emptyRow, 1, 1, testSheet.getMaxColumns());
var vals = range.getValues();
// Get the index of the first empty cell from the waarden array of values
var emptyCell = vals[0].indexOf("");
// log the index of empty cell
ss.toast("The index of the first empty cell is: " + emptyCell.toString());
Logger.log("The index of the first empty cell is: %s", emptyCell);
return emptyCell;
}
try this code:
function onOpen() {
var spreadsheet = SpreadsheetApp.getActive();
var menuItems = [
{name: 'Analyze and flip data selection', functionName: 'flipSelection'},
];
spreadsheet.addMenu('Scripts', menuItems);
}
function flipSelection()
{
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet= ss.getSheetByName("src");
var destSheet= ss.getSheetByName("dest");
//get datas
var datas = sheet.getDataRange().getValues();
var destDatas = [];
var row = [];
var max = 0;
var i = 0;
//start is to check if it's the beginning of parse meaning no previous data
var start = true;
//parse columns first then rows
for (var c = 0; c < datas[0].length; c++)
{
for (var r = 0; r < datas.length; r++)
{
//if cell is blank or beginning of new column
if (datas[r][c] == "" || !r)
{
i = 1;
//if it's not the beginning of program then add previous row to final data array.
if (!start)
{
destDatas.push(row);
}
//if it's a blank cell then add skip it
if (r)
r++;
//create a new row with first value inside of it
row = [datas[r][c]];
start = false;
}
//if it's not the beginning of a new column and the cell is not empty
else
{
//increment index columns of one
i++;
// Basically if there's more value than last time then set column length to column index
if (i >= max)
max = i;
//add new data to row array
row.push(datas[r][c])
}
}
}
//condition to push previous and last row into final data array
if(row)
destDatas.push(row);
//set final data array length to the same size
for(var i = 0; i < destDatas.length; i ++)
for (var j = (destDatas[i].length); j < max; j++)
destDatas[i].push("");
//set destination range and set values.
destSheet.getRange(1, 1, destDatas.length, max).setValues(destDatas);
}

Google Apps Script for Removing Rows Containing Part of a Keyword

I want to delete the rows on Google Sheets that contain employees, tests, irrelevant submissions, and duplicate entries. My code needs to be standalone so that it can be used across my workplace.
Specifically, I want to:
Remove any rows that contain an email address belonging to a certain organization (ex: any email address that ends in #domainname.com). I've been using a piece of code to delete rows containing three specific email addresses belonging to my coworkers, but I was hoping to find a way to delete all employees in one sweep without coding in each individual email. Here's the code I've been using:
function delVtlEm() {
var sheet = SpreadsheetApp.getActiveSheet();
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[1] == 'isaac#domainname.com' ||
row[1] == 'danni#domainname.com' ||
row[1] == 'georgia#domainname.com') {
sheet.deleteRow((parseInt(i) + 1) - rowsDeleted);
rowsDeleted++;
}
}
}
Remove any rows that contain the word "login" from a comment section where "login" might be only part of the copy in that column. For example, someone might fill out a Contact Us form and ask in the comment section for help with their login info - but this isn't a qualified lead for my purposes. Their message may be "Hey, can you help me with my login?" or some other similar phrasing, which is why I want to delete any row containing "login" in any capacity.
Please let me know if you have any ideas or suggested code!
I have implemented the following smartDelete() function based on your code.
This function allows you to achieve the following,
Identify any number of domains (in badDomains array) to delete its corresponding rows.
Identify any number of words (in badWords array) to delete its corresponding rows.
Both of the two search criteria above are case-insensitive; you can change that by changing the regular expression modifier (stored in regExpModifiers) to "" or Null.
Actions above can be taken on three different columns (stored in fnameColumnNumber, emailColumnNumber and companyColumnNumber)
Let me know if you face any issues or have any feedback.
function smartDelete() {
// smartDelete settings goes here,
var badDomains = ["vtldesign\\.com", "parterreflooring\\.com"];
var badWords = ["Vital", "Parterre", "test"];
var fnameColumnNumber = 0;
var emailColumnNumber = 1;
var companyColumnNumber = 3;
var regExpModifiers = "i";
// Gain access data in the sheet
var sheet = SpreadsheetApp.getActiveSheet();
var rows = sheet.getDataRange();
var numRows = rows.getNumRows();
var values = rows.getValues();
var rowsDeleted = 0;
var deleteAction = false;
// delete procedure
for (var i = 0; i <= numRows - 1; i++) {
var row = values[i];
deleteAction = false;
// check bad words
for (var j = 0; j <= badWords.length - 1; j++) {
var myPattern = new RegExp(badWords[j], regExpModifiers);
var status = row[fnameColumnNumber].toString().match(myPattern);
if (status) {
// match found, mark this row for delete
deleteAction = true;
break;
};
};
// check bad domains
for (var j = 0; j <= badDomains.length - 1; j++) {
var myPattern = new RegExp(badDomains[j], regExpModifiers);
var status = row[emailColumnNumber].toString().match(myPattern);
if (status) {
// match found, mark this row for delete
deleteAction = true;
break;
};
};
// check bad words
for (var j = 0; j <= badWords.length - 1; j++) {
var myPattern = new RegExp(badWords[j], regExpModifiers);
var status = row[companyColumnNumber].toString().match(myPattern);
Logger.log(status)
if (status) {
// match found, mark this row for delete
deleteAction = true;
break;
};
};
// execute delete.
if (deleteAction) {
sheet.deleteRow((parseInt(i) + 1) - rowsDeleted);
rowsDeleted++;
};
};
}
You can use indexOf('what to find') to look for a partial string. Also, don't delete rows in the Sheet individually. That is inefficient. Delete elements (rows) from the array, clear the sheet tab, and then set all the new values.
function delVtlEm() {
var i,row;
var sheet = SpreadsheetApp.getActiveSheet();
var rows = sheet.getDataRange();
var numRows = rows.getNumRows();
var values = rows.getValues();
var rowsDeleted = 0;
var arrayOfStringsToFind = ["whatToLookFor","whatToLookFor2","whatToLookFor3"];
for (i = 0; i <= numRows - 1; i++) {
row = values[i];
column1Value = row[0];//Get the value of column A for this row
column2Value = row[1];
column3Value = row[2];
if (arrayOfStringsToFind.indexOf('column1Value') !== -1) {
values.splice(i,1);//
}
if (column2Value.indexOf('#vtldesign.com') !== -1) {
values.splice(i,1);//Remove one element in the data array at index i
}
if (column3Value.indexOf('whatToLoookFor') !== -1) {
values.splice(i,1);//
}
}
sheet.clearContents();//clear the contents fo the sheet
sheet.getRange(1, 1, values.length, values[0].length);//Set new values
}