I need to loop through all rows/columns in a sheet and remove rows contain certain words. Ideally, I would search through the sheet using a regular expression, but just finding a string would help get me moving. I'm seeing a lot of posts on Stack Overflow about finding and deleting empty rows, but can't find anything about searching an entire sheet and deleting a row if found.
This is what I have so far:
/* Delete rows */
function deleteRows() {
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var rows = sheet.getDataRange();
var numRows = rows.getNumRows();
var values = rows.getValues();
var deleted = 0; // Counter (don't need if we loop backwards)
var regExp = new RegExp('word');
for (var row = 0; row < values.length; row++) {
var regExpMatch = values[row][1].match(regExp);
if (regExpMatch.length > 0) {
sheet.deleteRow(row + 1 - deleted);
deleted++;
}
}
SpreadsheetApp.flush();
};
However this only searches Column B, and it throws an error "TypeError: Cannot read property 'length' from null" even though "word" exists in Column B in my spreadsheet. And if I do a simpler version like:
/* Delete rows */
function deleteRows() {
var sheet = SpreadsheetApp.getActiveSheet();
var rows = sheet.getDataRange();
var numRows = rows.getNumRows();
var values = rows.getValues();
var deleted = 0; // Counter (don't need if we loop backwards)
for (var row = 0; row < values.length; row++) {
if (values[row][1].search("WordThatExistsInOneRow")) {
sheet.deleteRow(row);
deleted++;
}
}
SpreadsheetApp.flush();
};
It starts deleting every row, even if "WordThatExistsInOneRow" only shows up in 1 row.
You have not wrote your loop logic correctly, you need to loop over each row then each column in that row and then assign the row index for deletion which must be performed in revere order. Try this:
/* Delete rows */
function deleteRows() {
var sheet = SpreadsheetApp.getActiveSheet();
var rows = sheet.getDataRange();
var numRows = rows.getNumRows();
var values = rows.getValues();
var toDelete = [];
for (var row = 0; row < values.length; row++) {
for(var column = 0;column<values[row].length;column++){
if (values[row][column].indexOf("WordThatExistsInOneRow") > -1){
toDelete.push(row);
}
}
}
for(var deleteRow = toDelete.length-1; deleteRow >= 0;deleteRow--){
sheet.deleteRow(toDelete[deleteRow]+1);
}
SpreadsheetApp.flush();
};
Related
I am trying to write a function that takes a Google Sheet ID as an input and parses the spreadsheet, extracting URL links out of the cells when encountered. I wrote the following function based off of this answer and this blog post.
function getLinksFromSheet(sheetId){
var ss = SpreadsheetApp.openById(sheetId); //general case
var sheets = ss.getSheets();
sheets.forEach(sheet => {
var rangeData = sheet.getDataRange();
var lastColumn = rangeData.getLastColumn();
var lastRow = rangeData.getLastRow();
var searchRange = sheet.getRange(1,1, lastRow, lastColumn);
var rangeValues = searchRange.getRichTextValues();
for (var i = 0; i < lastRow; i++){
for (var j = 0 ; j < lastColumn; j++){
Logger.log(rangeValues[i][j].getLinkUrl())
}
}
});
The function works well for extracting links from cells where the only text contents of the cell are the link (for example http://google.com). If, however, the cell contains other text that isn't a part of the link (for example Link: http://google.com), then the function returns null. Does anybody know of a way to modify my funtion so that it will still return the link from cells with other text besides a link in them?
for (var i = 0; i < lastRow; i++){
for (var j = 0 ; j < lastColumn; j++){
const runs = rangeValues[i][j].getRuns();
for (const v of runs) {
Logger.log(v.getLinkUrl())
}
}
}
Reference:
getRuns()
Here is the code I have worked on- I have tried many different combinations and none of them are giving me the desired results- Can you please explain why my code is incorrect?
the code is iterating the the rows looking for rows larger than 0- if so, copy the row to the target sheet- However I am getting the wrong rows copied over?
function try2() {
var sheet1 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1');
var sheet2 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Final');
var lastrow = sheet2.getLastRow()+1;
var target = sheet2.getRange(lastrow,1,1,5);
var data = sheet1.getRange('E1:E500').getValues();
for(var i=1; i< data.length; i++) {
Logger.log(i)
if (data[i] > 0) {
Logger.log(data[i])
sheet1.getRange(i, 1, 1, 5).copyTo(target);
}
}
}
I updated the code to have the lastrow & target variables in the for loop with still undesired results.
function try2() {
var sheet1 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1');
var sheet2 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Final');
var data = sheet1.getRange('E1:E500').getValues();
for(var i=1; i< data.length; i++) {
var lastrow = sheet2.getLastRow()+1;
var target = sheet2.getRange(lastrow,1,1,5);
//Logger.log(i)
if (data[i] !== "") {
Logger.log(data[i])
sheet1.getRange(i, 1, 1, 5).copyTo(target);
}
}
}
Requirement:
Copy data from one sheet to the next free row of another sheet.
Solution:
function try2() {
var sheet1 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1');
var sheet2 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Final');
var targetLastRow = sheet2.getLastRow() + 1;
var dataLastRow = sheet1.getLastRow();
var dataRange = sheet1.getRange(1,5,dataLastRow);
var data = dataRange.getValues();
for(var i = 0; i < data.length; i++) {
if (data[i]) {
sheet1.getRange(i+1, 1, 1, 5).copyTo(sheet2.getRange(targetLastRow+i,1));
}
}
}
Explanation:
This script will loop through all of the rows in column E on your first sheet, and copy any that aren't blank across to your sheet "Final". By using dataLastRow to get the data. You save time over defining a set range E1:E500, the script will now only get the necessary amount of rows to run the copy whereas before it was running the for loop 500 times no matter what.
The data is copied to targetLastRow+i, which is the next available row of sheet "Final", incremented by 1 every time the loop completes.
Code that worked altering #ross code- Thanks
function try3() {
var sheet1 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1');
var sheet2 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Final');
var targetLastRow = sheet2.getLastRow() + 1;
var dataLastRow = sheet1.getLastRow();
var dataRange = sheet1.getRange(1,5,dataLastRow);
var data = dataRange.getValues();
for(var i = 0; i < data.length; i++) {
Logger.log(data[i])
if (data[i] > 0) {
Logger.log(data[i])
sheet1.getRange(i+1, 1, 1, 5).copyTo(sheet2.getRange(targetLastRow+i,1));
}
}
}
I am looping through a range of data in a Google spreadsheet in JavaScript.
The problem I'm having is I have some merged cells that have the same value, for example C:6 to E:6 all have the same value as those 3 cells are merged.
So in my loop it will show C:6 = 'Focus and targeting' D:6 = '' and E:6 = '' What I'm trying to do is in my loop assign the 2nd and 3rd values to the first cell value.
Here is what I have:
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Audit');
var range = sheet.getDataRange();
var numRows = range.getNumRows();
var numCols = sheet.getMaxColumns();
var values = range.getValues();
var count = 0;
for (var i = 2; i <= numCols - 2; i++) {
var questionCategory = values[5][i];
if(questionCategory == undefined) {
continue;
}
if(questionCategory == "") {
// need to assign 2nd and 3rd cells of merged list to have the value of 1st cell in merged list
}
count++;
}
Here is what the data looks like:
Any ideas how I can achieve this?
Here is custom function to get merged cell value:
function getMergedCellValue(cellRange)
{
if (!cellRange.isPartOfMerge())
{
return cellRange.getDisplayValue();
}
return cellRange.getMergedRanges()[0].getDisplayValue();
}
And updated code using this function:
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Audit');
var range = sheet.getDataRange();
var numRows = range.getNumRows();
var numCols = range.getNumColumns();
var values = range.getValues();
var count = 0;
var questionRowNum = 6;
for (var i = 0; i < numCols; i++)
{
// assign 2nd and 3rd cells of merged list to have the value of 1st cell in merged list
var questionCategory = values[questionRowNum-1][i] || getMergedCellValue(sheet.getRange(questionRowNum, i+1));
count++;
}
I'm trying to transpose (copy column content into a row) a range from one sheet to another. Yet the catch is that each row has a different number of columns populated, and I don't want to have blank columns copied over into the output sheet.
So I was trying to do the following:
Go to 'import manipulation' tab
row by row (in a loop), check what the last POPULATED column is
Then col by col (second loop), copy each cell's value then paste it onto "Transpose" sheet in the first available row
Keep doing this until the last Populated column was copied over, and then move onto the next row
Repeat
I've ran the debugger and nothing comes up, however when I run the function it doesn't paste anything into the "Test" sheet. Any suggestions?
function transpose(sh) {
var sh = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Import Manipulation");
var lastrow = sh.getLastRow();
var numrows = lastrow;
for(var i= 1; i<= numrows; i++) {
Logger.log(i);
var rowrange = sh.getRange(i, 50);
Logger.log(rowrange);
var lastcol = rowrange.getValues().length;
for(var j=5; j<= lastcol; j++) {
var colrange = sh.getRange(i,j);
var copycell = colrange.getValue();
var pastesheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Test");
var pasterow = pastesheet.getLastRow();
Logger.log(pasterow);
var pastecell = pastesheet.getRange(pasterow + 1, 1);
Logger.log(pastecell);
var doit = pastecell.setValue(copycell);
doit(pastecell);
}
}
}
I was able to do it so that it transposes, and it's working (albeit probably not the most efficient).... appreciate suggestions for how to make it more efficient!
function transpose(sh) {
var sh = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Import Manipulation");
var lastrow = sh.getLastRow();
var numrows = lastrow;
var lastcol = sh.getLastColumn();
for(var i= 1; i<= numrows; i++) {
Logger.log(i);
var rowrange = sh.getRange(i,1,1,lastcol);
Logger.log(rowrange);
for(var j=5; j<= lastcol; j++) {
var colrange = sh.getRange(i,j);
var copycell = colrange.getValue();
var pastesheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Transpose");
var pasterow = pastesheet.getLastRow();
Logger.log(pasterow);
var pastecell = pastesheet.getRange(pasterow + 1, 1);
Logger.log(pastecell);
pastecell.setValue(copycell);
}
}
}
This line of code:
var rowrange = sh.getRange(i, 50);
Is only getting one cell. On the first loop, it gets the cell in row 1 column 50.
The variable lastcol in this line:
var lastcol = rowrange.getValues().length;
will always be 1. getValues() gets a 2D array and in this case it will be [[cellValue]]
I created some new code:
function transpose(sh) {
var arrayThisRow,cellValue,i,j,lastCol,lastrow,numrows,pastesheet,
rowrange,rowValues,sh;
sh = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Import Manipulation");
pastesheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Test");
lastrow = sh.getLastRow();
lastCol = sh.getLastColumn();
Logger.log('lastCol: ' + lastCol);
numrows = lastrow;
arrayThisRow = [];
for(i=1; i<= numrows; i++) {
//Logger.log(i);
rowrange = sh.getRange(i,1,1,lastCol);
//Logger.log(rowrange);
rowValues = rowrange.getValues();//
rowValues = rowValues[0];//There is only 1 inner array
for(j=0; j <= lastCol; j++) {
cellValue = rowValues[j];
if (cellValue) {
arrayThisRow.push(cellValue);
} else {
break;//Stop pushing values into the array as soon as a blank cell is found
}
}
}
Logger.log('arrayThisRow: ' + arrayThisRow)
if (arrayThisRow.length > 0) {
pastesheet.appendRow(arrayThisRow)
}
}
I can't get this function working. It's has to copy all the values of spreadsheet "A" to spreadsheet "B".
function triggerOnTime() {
var SpreadSheetKeyA = "142WNsfQQkSx4BuNhskDM9aXD9ylRHNZh34oO5TBTt1g";
var SpreadSheetKeyB = "1h8fDwCUUPHRdmTHu-5gMyqU294ENZxCZcHCNCuN6r_Y";
var sheet1 = SpreadsheetApp.openById(SpreadSheetKeyA).getActiveSheet();
var sheet2 = SpreadsheetApp.openById(SpreadSheetKeyB).getActiveSheet();
var data = sheet1.getDataRange().getValues();
var array = [];
for (var y = 1; y < data.length; y++) {
for (var x = 0; x < 35; x++){
array.push(data[y][x]);
}
Logger.log(array);
sheet2.appendRow(array);
sheet1.deleteRow(y);
}
}
It now copy's two or three values, but copy them multiple times (random). The function must also delete the copied values from sheetA. But it only deletes the values that are added to sheetB.
EDIT (Updated code)
function triggerOnTime() {
var SpreadSheetKeyA = "142WNsfQQkSx4BuNhskDM9aXD9ylRHNZh34oO5TBTt1g";
var SpreadSheetKeyB = "1h8fDwCUUPHRdmTHu-5gMyqU294ENZxCZcHCNCuN6r_Y";
var sheet1 = SpreadsheetApp.openById(SpreadSheetKeyA).getActiveSheet();
var sheet2 = SpreadsheetApp.openById(SpreadSheetKeyB).getActiveSheet();
var data = sheet1.getDataRange().getValues();
var array = [];
for (var y = 0; y < data.length; y++) {
for (var x = 0; x < 35; x++){
array.push(data[y][x]);
}
sheet2.appendRow(array);
sheet1.deleteRow(y+1);
array = [];
}
}
After playing around a little, I have found a fix for you (albeit a hack, I guess. Im sure there's a better way of doing it)
Whats happening is, on line 14 where you appendRow(array), you are appending the array at it's current state to the second sheet; basically creating a big pyramid of values of your array over time.
If sheet 1 contained something like this:
1,1,1
2,2,2
3,3,3
4,4,4
5,5,5
Your sheet 2 would get this appended:
1,1,1
1,1,1,2,2,2
1,1,1,2,2,2,3,3,3
... and so on.
You could do something like this:
function triggerOnTime() {
var SpreadSheetKeyA = "142WNsfQQkSx4BuNhskDM9aXD9ylRHNZh34oO5TBTt1g";
var SpreadSheetKeyB = "1h8fDwCUUPHRdmTHu-5gMyqU294ENZxCZcHCNCuN6r_Y";
var sheet1 = SpreadsheetApp.openById(SpreadSheetKeyA).getActiveSheet();
var sheet2 = SpreadsheetApp.openById(SpreadSheetKeyB).getActiveSheet();
var data = sheet1.getDataRange().getValues();
var array = [];
for (var y = 1; y < data.length; y++) {
for (var x = 0; x < 35; x++){
array.push(data[y][x]);
}
sheet2.appendRow(array);
sheet1.deleteRow(y);
array = []; //reset the array contents
}
}
The additional line will just reset the array and add the row you want to copy across.
Let me know if this works out for you.