Download images only from Filtered Cells - javascript

In my sheet I have more then 1000 image URL's. This code download all the images in there respective cells which is fine but It's too much data.
function getImages() {
const sheetName = "Daily Data"; // Please set the sheet name.
const cellA1Notation = "I2:I"; // Please set the cell range as a1Notation.
const imageWidth = 200; // Please set the width you want.
const imageHeight = 150; // Please set the height you want.
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
const dataRange = sheet.getRange(cellA1Notation + sheet.getLastRow());
const urls = dataRange.getValues().flat();
urls.forEach(function(url, i) {
if (url) {
const image = SpreadsheetApp.newCellImage().setSourceUrl(url).build();
const cell = sheet.getRange(i + 2, 9);
cell.setValue(image);
sheet.setRowHeight(cell.getRow(), imageHeight).setColumnWidth(cell.getColumn(), imageWidth);
}
});
}
So I apply the filter function in my sheet. The Problem is above code download all the images and I want to download images only from filtered cells. I tried this code but it's giving me error.
TypeError: filter.match is not a function
function getFilteredRange() {
const sheetName = "Daily Data"; // Please set the sheet name.
const cellA1Notation = "I2:I"; // Please set the cell range as a1Notation.
const imageWidth = 200; // Please set the width you want.
const imageHeight = 150; // Please set the height you want.
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
const dataRange = sheet.getRange(cellA1Notation + sheet.getLastRow());
const filter = sheet.getFilter();
const filteredRows = filter ? filter.getRange().getValues()
.map((row, index) => filter.match(index + 1) ? index : -1)
.filter(index => index >= 0) : [];
const urls = filteredRows.map(index => dataRange.getCell(index + 1, 1).getValue());
urls.forEach(function(url, i) {
const image = SpreadsheetApp.newCellImage().setSourceUrl(url).build();
const cell = sheet.getRange(filteredRows[i] + 2, 9);
cell.setValue(image);
sheet.setRowHeight(cell.getRow(), imageHeight).setColumnWidth(cell.getColumn(), imageWidth);
});
}
Is there is any solution for it. Either it download all the images in filtered area or any way I delete all the data outside filtered area except header then I Use my old code to download the images.

This is Google Apps, right? Are you looking for the getRange() method? As the error message says there is no filter.match() method. https://developers.google.com/apps-script/reference/spreadsheet/filter#getRange()

The script is an adaptation of the StackOverFlow answer to How to get last visible row, if rows are hidden or not hidden by filter in Google Sheets? by #Tanaike. But it should be noted that this is a very common topic and there are many more precedents and solutions available on this subject.
The features of this answer are:
allows the user to manually apply a filter (or not) to the data column.
uses isRowHiddenByUser and isRowHiddenByFilter to creates an array of urls displayed by the filter.
uses indexOf to loop through ALL the urls but process only those that are in the array of filtered urls.
function hiddenAndFilteredRows2() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheetName = "Sheet1"
var sheet = ss.getSheetByName(sheetName)
var cellA1Notation = "I2:I"; // Please set the cell range as a1Notation.
var dataRange = sheet.getRange(cellA1Notation + sheet.getLastRow());
// Logger.log("DEBUG: data range = "+dataRange.getA1Notation())
var data = dataRange.getValues();
// Logger.log(data) // DEBUG
var unhiddenArray = []
// note data starts at row 2, so test i+2
for (var i = 0; i < data.length; i++) {
if (sheet.isRowHiddenByUser(i+2)) {
// Logger.log("DEBUG: i:"+i+", row#"+(i+2)+", url:"+data[i][0]+" :hidden by user")
continue
}
if(sheet.isRowHiddenByFilter(i+2)) {
// Logger.log("DEBUG: i:"+i+", row#"+(i+2)+", url:"+data[i][0]+" :hidden by filter")
continue
}
// Logger.log("DEBUG: i:"+i+", row#"+(i+2)+", url:"+data[i][0]+" :not hidden")
unhiddenArray.push(data[i][0])
}
// Logger.log(unhiddenArray) // DEBUG
// apply code from "getImages"
var imageWidth = 200; // Please set the width you want.
var imageHeight = 150; // Please set the height you want.
var urls = data.flat()
// Logger.log(urls) // DEBUG
// loop through ALL the urls
// but only process those that are in the unhiddenArray
for (i=0;i<urls.length;i++){
// Logger.log("DEBUG: i:"+i+", row:"+(i+2)+", url:"+urls[i])
if (unhiddenArray.indexOf(urls[i]) !=-1){
var image = SpreadsheetApp.newCellImage().setSourceUrl(urls[i]).build();
var cell = sheet.getRange(i + 2, 9);
cell.setValue(image);
sheet.setRowHeight(cell.getRow(), imageHeight).setColumnWidth(cell.getColumn(), imageWidth);
}
}
}
RAW DATA
FILTERED DATA (text contains "400px")
unhiddenArray
Processed data

Related

Google Apps Script: Replace all text in slide table with values and formatting from sheets

Here is the description of my problem.
I have a range of placeholder text and its associated values in a spreadsheet. The placeholder text also exists in a slide presentation which gets replaced using the replacealltext function. However, the colors in the spreadsheet for the values do not change. Please see the examples below.
Google Apps Script:
// Google Sheets
var masterSheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet = masterSheet.getSheetByName('Monthly Report'); //gets the data from the active pubfile
var range = sheet.getRange('S1:T110');
var data = range.getValues();
var titleName = sheet.getRange('AA14:AA14').getValues().toString();
data.shift(); //removes the headers in the sheets
// Creating the new slide
var spreadsheetID = SpreadsheetApp.getActiveSpreadsheet().getId(); //Ensure Code is applied to active Pubfile
var spreadsheetFolder = DriveApp.getFileById(spreadsheetID);
var parentFolder = spreadsheetFolder.getParents();
var folderID = parentFolder.next().getId();
var reportTemplate = DriveApp.getFileById('SLIDE ID'); // Gets the report Template
var copiedTemplate = reportTemplate.makeCopy(titleName, DriveApp.getFolderById(folderID)); //Makes a copy of the orginal and saves it in the specified folder
var newPresoID = copiedTemplate.getId();
var skeleton = SlidesApp.openById(newPresoID); //opens the new template presentation
var slides = skeleton.getSlides(); //calls the new slides
return newSlide1();
function newSlide1() {
var slide1new = slides[0]; // defining the location of the new slide 1
data.forEach(function (row) {
var templateVariable = row[0]; // First column contains variable names
var templateValue = row[1]; // Second column contains values
slide1new.replaceAllText(templateVariable, templateValue); //replaces all the text that matches the placeholders
slide1new.
});
As you can see, this code just replaces the value but does not bring in the source formatting.
Here are snapshots of the original spreadsheet data and the slides:
Source Data
Destination Slide with Placeholders
Current Result
Desired Result
Please suggest a way to fix this issue. Thank you!
Here is an example of applying the colors of a spreadsheet cell to the table cells of a slide.
My spreadsheet looks like this.
My slide looks like this.
Code.gs
function myFunction() {
try {
let spread = SpreadsheetApp.openById("xxxxxxxxxxxxxxxxxxxxxxxxxxx");
let sheet = spread.getSheetByName("Sheet1");
let range = sheet.getRange(2,1,sheet.getLastRow()-1,2);
let values = range.getDisplayValues();
let colors = range.getFontColors();
let present = SlidesApp.getActivePresentation();
let slide = present.getSlides()[0];
let table = slide.getTables()[0];
for( let i=0; i<table.getNumRows(); i++ ) {
let row = table.getRow(i);
for( let j=1; j<row.getNumCells(); j++ ) {
let text = row.getCell(j).getText();
values.some( (value,k) => {
let l = text.replaceAllText(value[0],value[1]);
if( l > 0 ) {
text.getTextStyle().setForegroundColor(colors[k][1]);
return true;
}
return false;
}
);
}
}
}
catch(err) {
console.log(err);
}
}
And the final results look like this.
Reference
TableCell
Array.some()

getRange('A1:B') keeps going for ages - part of delete rows when cell does not contain value

My sample sheet
I have 2 columns, A contains units & B data. I want to delete entire rows from A & B where text in B does not contain '-'.
I have other data in the other columns in the sheet, so this should only apply to A & B.
I normally manually filter and delete these rows.
Following the script in this question, when I used exactly this but with "| My Text Here" replace with "-", it worked, but a lot of the data that should be there were also deleted. So when I should have had 300 rows of data left, I only had 124 after running the script.
How to delete rows that do not containing specific text within A column using Google Sheets App?
function deleteFunction(){
//declarations
var ss = SpreadsheetApp.getActive();
var sheet = ss.getSheetByName('Sheet1');
var dataRange = sheet.getDataRange();
var values = dataRange.getValues();
for(var i=values.length-1;i>0;i--){
var myValue=values[i][0];
Logger.log(i);
if( myValue.indexOf("-")==-1){
Logger.log(myValue);
sheet.deleteRow(i+1);
}
}
}
When I change the dataRange to just getRange('A1:B'); the function keeps going for ages without results.
I changed it to .getRange(1, 2, dataRange.getLastRow(), dataRange.getLastColumn()); and that deletes ALL the data in the range.
Basically, at this point I'm confused, I keep changing the ranges to try to see results.
I'm a newbie to this so I'm not sure where I go wrong.
Try it this way:
function myFunction() {
const ss = SpreadsheetApp.getActive();
const shsr = 3;//data startrow atleast in your sample
const sh = ss.getSheetByName('Sheet1');
const rg = sh.getRange(shsr, 1 , sh.getLastRow() - shsr + 1, 2);
const vs = rg.getValues();
let d = 0;
vs.forEach((r, i) => {
if (!~r[1].toString().indexOf('-')) {
sh.deleteRow(i + shsr - d++)
}
});
}
You need to count the number of rows that you delete because all of the row move up as you delete them. So you have to keep subtracting a larger offset as you delete more and more lines because indexing of the array does not change. Also you were using values[i][0] which is column one and your data indicate it should have been column 2. And finally by using sh.getDataRange() you start running your code at line one instead of line 3 where your data starts.
or this way
This method is a little quicker because you are not deleting one row at a time.
function myFunction() {
const ss = SpreadsheetApp.getActive();
const shsr = 3;
const sh = ss.getSheetByName('Sheet1');
const rg = sh.getRange(shsr, 1 , sh.getLastRow() - shsr + 1, 2);
const vs = rg.getValues();
let d = 0;
let o = [];
vs.forEach((r, i) => {
if (~r[1].toString().indexOf('-')) {
o.push(r)
}
});
rg.clearContent();
sh.getRange(shsr, 1, o.length,o[0].length).setValues(o);
}
In the latter case we are keeping load the rows that we want into an output array and then removing all lines from the shsr to the bottom and replacing them with the wanted lines.

Is it possible to insert a custom formula in conditional format dynamically by searching for a column name?

I'm attempting to build a function in google script that would allow me to code in certain column names (as these will remain constant), and to have said function find that column by the name, and create/insert a custom formula via conditional formatting to highlight duplicates in that column. The formula to find duplicates is =COUNTIF(E:E,E1)>1 (using column E as an example).
So to re-state, what I'm trying to do is:
Have a function that inserts a new conditional format rule to highlight duplicates in a row
Have the formula for the new conditional format be dynamic in case the column moves between data sets (although the column name will be the same)
Have this function insert the conditional format into a column by name, instead of the number
I've tried to find something similar to this on stackoverflow but not finding much luck, just a few posts that are:
Custom Formula in Condition Format Rule Builder Script
Get column values by column name not column index
So this sheet would find the column "WOW":
Desired outcome/Would look like this after the function has ran (Highlight E2 and E2 in the WOW column):
So my attempt at making this code is below, however I'm hoping someone has a better idea as it's not quite working and this seems to be advanced coding (and I'm a newbie)
function conditionalformat1(){
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('sheet1');//need to call the tab name as I have many tabs
var colName = 'WOW' //specific column name to have this function search for
var sheetName = 'sheet1'
var newRule = SpreadsheetApp.newConditionalFormatRule()
.whenFormulaSatisfied('=COUNTIF(E:E,E1)>1')//this should be dynamic instead what it is now...not sure if we can use R1C1 notation?
.setBackground('red')
.setRanges(getColByName)
.build()//similar setup to (https://stackoverflow.com/questions/50911538/custom-formula-in-condition-format-rule-builder-script) #Patrick Hanover & https://developers.google.com/apps-script/reference/spreadsheet/conditional-format-rule-builder#whenFormulaSatisfied(String)
var rules = sheet.getConditionalFormatRules();
rules.push(conditionalformat1);
sheet.setConditionalForatRules(rules);
function getColByName(colName) {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('sheet1');
var colName = 'WOW'
var data = sheet.getRange("1:1000").getValues();//have it search the entire sheet?
var col = data[0].indexOf(colName);
if (col != -1) {
return data[row-1][col];
}
}// via the question & author https://stackoverflow.com/questions/36346918/get-column-values-by-column-name-not-column-index #Leonardo Pina
}//end of conditionalformat1 conditional formatting
Thanks for the help in advance, this will be great to learn how to have functions find columns by name and execute items.
You do not need any code, just use the conditional formatting formula
=AND(a$1="WOW";countif(A$2:A;a2)>1)
Get column number using your current function getColumnByName
Get column letter from the column number
Create a range string from column number
Use the range string in your custom formula and the range variable in conditional formatting
function conditionalformat0() {
const sheetName = 'sheet1';
const colName = 'WOW'; //specific column name to have this function search for
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName); //need to call the tab name as I have many tabs
const c2l = (n) =>
/*Column to Letter: 0 => A*/
n >= 26
? String.fromCharCode(64 + n / 26) + c2l(n % 26)
: String.fromCharCode(65 + n);
const getColByName = (colName, sheet) => {
const data = sheet.getRange(`1:${sheet.getLastColumn()}`).getValues();
const col = data[0].indexOf(colName);
if (col !== -1) return col;
throw new Error('Column not found');
};
const colLetr = c2l(getColByName(colName, sheet));
const rangeA1 = `${colLetr}:${colLetr}`;
const newRule = SpreadsheetApp.newConditionalFormatRule()
.whenFormulaSatisfied(`=COUNTIF(${rangeA1},${colLetr}1)>1`)
.setBackground('red')
.setRanges([sheet.getRange(rangeA1)])
.build();
const rules = sheet.getConditionalFormatRules();
rules.push(newRule);
sheet.setConditionalFormatRules(rules);
}
/*
If you want to find columns by name the best thing to do is to build an object that does it for you so that you don't have to call and additional function every time you need the column name.
First of all you have to know the row that the column names are on. I call this the header row
*/
function getColumnHeaders() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName('SheetName');
const hr = 1;//header row
const hA = sh.getRange(hr, 1, 1, sh.getLastColumn()).getValues().flat();
const col = {};//the object that converts column names to numbers
hA.forEach((h, i) => col[h] = i + 1);
/* this gives you column numbers however often what you really want are the column indices so that you can use them to access data in rows of the 2 dimensional arrays like the ones returned by getValues() function in this case you would do it the following way
*/
const idx = {}; //the object that does the conversion for you from column names to indices
hA.forEach((h, i) => idx[h] = i);
/* if you want both then you can do it this way */
hA.forEach((h, i) => col[j] = i + 1; idx[h] = i;);
/* Now if you wish to access data from a spreadsheet with a header row by using header names instead of indexes you can */
const data = sh.getRange(startRow, startCol, sh.getLastRow() - startRow + 1, sh.getLastColumn() - sc + 1).getValues();
data.forEach(r =>
r[idx["column name"]];//this accesses the value in the current row for the column name that you enter and it does not require you to do an additional function call to do it because it's already in the object and most like the object is located in the cpu's cache so this is much faster
)
}
I don't consider this to be a complete answer to the question I just wanted to point out a way to do this that doesn't require function calls because unnecessary function calls inside of your data loops will greatly decrease performance.
I've fixed your code a bit. It should work to some extent. Try it:
function conditionalformat() {
var sheetName = 'Sheet1';
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
var colName = 'WOW';
var range = sheet.getRange(1,getColByName(colName,sheet),sheet.getLastRow());
var formula = '=COUNTIF(E:E,E1)>1';
var newRule = SpreadsheetApp.newConditionalFormatRule()
.whenFormulaSatisfied(formula)
.setBackground('red')
.setRanges([range])
.build();
var rules = sheet.getConditionalFormatRules();
rules.push(newRule);
sheet.setConditionalFormatRules(rules);
}
function getColByName(colName,sheet) {
var data = sheet.getDataRange().getValues();
var col = data[0].indexOf(colName);
if (col != -1) return col+1;
}
But I don't understand what do you want to do with the formula =COUNTIF(E:E,E1)>1. Should it changing somehow? How?
And just in case, here is the pure JS function (based on Amit Agarwal's solution) to get column letter(s) from a number:
function get_col_letter(column) {
var col_letter = '';
let block = column;
while (block >= 0) {
col_letter = String.fromCharCode((block % 26) + 65) + col_letter;
block = Math.floor(block / 26) - 1;
}
return col_letter;
};
console.log(get_col_letter(0)); // 'A'
console.log(get_col_letter(1)); // 'B'
console.log(get_col_letter(25)); // 'Z'
console.log(get_col_letter(26)); // 'AA'
Update
I've added the option to change letters in formula by given column:
function conditionalformat() {
var sheetName = 'Sheet1';
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
var colName = 'WOW';
var colNum = getColNum(colName,sheet);
var colLetter = getColLetter(colNum);
var range = sheet.getRange(`${colLetter}1:${colLetter}`);
var formula = `=COUNTIF(${colLetter}:${colLetter},${colLetter}1)>1`;
var newRule = SpreadsheetApp.newConditionalFormatRule()
.whenFormulaSatisfied(formula)
.setBackground('red')
.setRanges([range])
.build();
// sheet.clearConditionalFormatRules(); // remove old formatting
var rules = sheet.getConditionalFormatRules();
rules.push(newRule);
sheet.setConditionalFormatRules(rules);
}
// get number column from its name
function getColNum(colName,sheet) {
var data = sheet.getDataRange().getValues();
var col = data[0].indexOf(colName);
if (col != -1) return col+1;
}
// get letter column from its number
function getColLetter(column) {
var col_letter = '';
let block = column - 1;
while (block >= 0) {
col_letter = String.fromCharCode((block % 26) + 65) + col_letter;
block = Math.floor(block / 26) - 1;
}
return col_letter;
};

drive.properties for loop with google sheets

I have two functions in a google sheet that are meant to loop through a single column containing the file IDs of files in google drive, issuing a Properties.get to retrieve a single property "LastReview" for each document and paste all of the LastReview times in the next available column.
I'm having trouble getting the loop in "loopForMetadata" to work. I want it to acquire a list of all the LastReview times associated with each fileID and then post that to the next available column so that all of the LastReview times align with the fileIDs.
function getProperty(fileId) {
var propertyKey = 'LastReview'
var fileId = '1UaQkJU8r1kE9sxpFg6OD8aOuUCoRnSpB9Agg_R9HJ3s'
var response = JSON.stringify(Drive.Properties.get(fileId, 'LastReview', { visibility: 'PUBLIC' }).value);
var key = "value";
var resposeNoQuote = response.replace(/\"/g, "")
Logger.log(resposeNoQuote);
}
function loopForMetadata() {
var columnNeeded, data, lastColumn, sh;
sh = SpreadsheetApp.getActiveSheet();
lastColumn = sh.getLastColumn();
data = sh.getRange(1, 1, 1, lastColumn).getValues();//Get 2D array of all values in row one
data = data[0];//Get the first and only inner array
columnNeeded = data.indexOf('ID') + 1;//Arrays are zero indexed- add 1
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var rangeData = sheet.getDataRange();
var lastColumn = rangeData.getLastColumn();
var lastRow = rangeData.getLastRow();
var searchRange = sheet.getRange(2, columnNeeded, lastRow - 1, 1);
// Get array of values in the search Range
var rangeValues = searchRange.getValues();
// Loop through array and if condition met, add relevant
// background color.
var resultValues = []
for (i = 0; i < rangeValues.length; i++) {
resultValues.push(getProperty(rangeValues[i]));
Utilities.sleep(10);
}
Logger.log(resultValues);
};
I believe your goal as situation as follows.
You want to retrieve the values using the key of LastReview in the property from the files of fileId.
You want to put the retrieved values to the same row of fileId in the next available column.
You want to achieve this using Google Apps Script.
Modification point:
In your script,
getProperty() doesn't return the values.
resultValues is not put to the Spreadsheet.
When you want to retrieve the value of the key LastReview, Drive.Properties.get(fileId, 'LastReview', { visibility: 'PUBLIC' }).value directly returns the value.
As an important point, when the file of fileId has not property of the key LastReview, it seems that Drive.Properties.get() occurs an error. So in this modification, as a simple workaround, I used the try catch.
When above points are reflected to your script, it becomes as follows.
Sample script:
function loopForMetadata() {
var columnNeeded, data, lastColumn, sh;
sh = SpreadsheetApp.getActiveSheet();
lastColumn = sh.getLastColumn();
data = sh.getRange(1, 1, 1, lastColumn).getValues();//Get 2D array of all values in row one
data = data[0];//Get the first and only inner array
columnNeeded = data.indexOf('ID') + 1;//Arrays are zero indexed- add 1
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var rangeData = sheet.getDataRange();
var lastColumn = rangeData.getLastColumn();
var lastRow = rangeData.getLastRow();
var searchRange = sheet.getRange(2, columnNeeded, lastRow - 1, 1);
var rangeValues = searchRange.getValues();
var resultValues = []
// I modified below script.
for (i = 0; i < rangeValues.length; i++) {
var fileId = rangeValues[i];
var value = "";
try {
value = Drive.Properties.get(fileId, 'LastReview', { visibility: 'PUBLIC' }).value;
} catch(e) {}
resultValues.push([value]);
// Utilities.sleep(10); // I'm not sure whether this is required.
}
sheet.getRange(2, lastColumn + 1, resultValues.length, 1).setValues(resultValues);
Logger.log(resultValues);
};
Note:
Please confirm whether Drive API is enabled at Advanced Google services, again.
Reference:
Properties: get

Remove duplicates across multiple sheets

I want to remove duplicates across 2 different sheets.
I have my active sheet, and I want to remove duplicates that already exist in my sheet "Blacklist". I want to run this process for both Column A and Column B (or simply for any values across the entire sheets). When a duplicate is found, I want to leave the row in tact but replace the value with '' (e.g. an empty cell).
I have a working version I mangled together, but only for the active sheet.
N.B. it's the findDuplicate function that I use, the removeDuplicate function I left there not to mess anything up :)
// this is a Google Apps Script project
function onOpen() {
var spreadsheet = SpreadsheetApp.getActive();
var menuItems = [
{ name: 'Find duplicates...', functionName: 'findDuplicate' },
{ name: 'Remove duplicates...', functionName: 'removeDuplicate' }
];
spreadsheet.addMenu('Duplicates', menuItems);
}
function removeDuplicate() {
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getActiveRange();
var data = range.getValues();
var rowNum = range.getRow();
var columnNum = range.getColumn();
var columnLength = data[0].length;
var uniqueData = [];
var duplicateData = [];
// iterate through each 'row' of the selected range
// x is
// y is
var x = 0;
var y = data.length;
// when row is
while (x < y) {
var row = data[x];
var duplicate = false;
// iterate through the uniqueData array to see if 'row' already exists
for (var j = 0; j < uniqueData.length; j++) {
if (row.join() == uniqueData[j].join()) {
// if there is a duplicate, delete the 'row' from the sheet and add it to the duplicateData array
duplicate = true;
var duplicateRange = sheet.getRange(
rowNum + x,
columnNum,
1,
columnLength
);
duplicateRange.deleteCells(SpreadsheetApp.Dimension.ROWS);
duplicateData.push(row);
// rows shift up by one when duplicate is deleted
// in effect, it skips a line
// so we need to decrement x to stay in the same line
x--;
y--;
range = sheet.getActiveRange();
data = range.getValues();
// return;
}
}
// if there are no duplicates, add 'row' to the uniqueData array
if (!duplicate) {
uniqueData.push(row);
}
x++;
}
}
function findDuplicate() {
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getActiveRange();
var data = range.getValues();
var rowNum = range.getRow();
var columnNum = range.getColumn();
var columnLength = data[0].length;
var uniqueData = [];
// iterate through each 'row' of the selected range
for (var i = 0; i < data.length; i++) {
var row = data[i];
var duplicate = false;
// iterate through the uniqueData array to see if 'row' already exists
for (var j = 0; j < uniqueData.length; j++) {
if (row.join() == uniqueData[j].join()) {
// if there is a duplicate, highlight the 'row' from the sheet
duplicate = true;
var duplicateRange = sheet.getRange(
rowNum + i,
columnNum,
1,
columnLength
);
duplicateRange.setValue('');
}
}
// if there are no duplicates, add 'row' to the uniqueData array
if (!duplicate) {
uniqueData.push(row);
}
}
}
Thanks so much for your help! I've been at this for a few hours and figured I should just ask the experts for advice :)
The first lines of both your removeDuplicate and findDuplicate function seems indeed to indicate that you refer to the active spreadsheet / sheet / range
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getActiveRange();
var data = range.getValues();
If you want to be able to use the same function for a given spreadsheet / sheet / range which is not the active one, you will need to use other functions than the getActiveXXX().
For example, to get the sheet named "Blacklist", you should use
sheet = spreadsheet.getSheetByName("Blacklist")
(see also https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet#getsheetbynamename)
If you want to access a specific range which differs from the active range, you should use the getRange method (see also https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet#getrangea1notation)
Note that getRange method can be used in different ways, e.g.
getRange("A1:D4"), getRange(1, 1, 3, 3) (the parameters being respectively startRow, startColumn, numRows,numColumns)
Additionally, if you don't want to hardcode the last line of your 2 columns, you will most probably need this function to find the last line in the code :
https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet#getlastrow
(there is also an example there showing how to use getRange() in combination with getLastRow()).
I hope this will help you going further.
Please note that I didn't check the rest of your code and just assumed that your deduplication logic works fine as you mentioned it in your commment.
Good luck !

Categories