increment by letter in google-apps-script - google-apps-script

I have data in multiple columns from rows 3-15. I want to find all the unique values in that data set and have it outputted in one column.
The unique function only does one column at a time so it does not work for my purposes.
I am writing a script that will open a new sheet and copy and paste the values into the same column (combines data from multiple columns into one longer column). I will then run the unique function on the single column containing all the data to get an output of all the unique values in my original dataset (Items!B3:Z15)
I have the code written but cannot figure out how to increment a character. If there is a better way to achieve this I am open to suggestions, but I am also curious if it is possible to increment letters.
Thanks for the help, here is my current code:
function uniqueIngredients() {
var book = SpreadsheetApp.getActiveSpreadsheet();
var r=1; // starting with row 1 new sheet
var d='B';
for (i=1; i<4; i++){ //testing by running 3 iterations
var m = 'Ingredients!A'+r; //Intially return Ingredients!A1 then incremented by 11 ex A12
var c = 'Items!'+d+'4:'+d+'15'; // Initially return Items!B3:B15 then incremented by letter ex Items!C3:C15
r = r+12; // add 12 spaces before next copy (max number of ingredients for each item)
d++;
book.getRange(c).copyTo(book.getRange(m), {contentsOnly:true});
}
};

You can use an alternative parameter specification for the getRange() method, which uses column numbers rather than A1 string notation. This would be easier to increment.
That being said, as you are copying values only, it would probably be better (more efficient) to get the source range in a block, and use Javascript to convert into a one-column array, and then set the values:
function uniqueIngredients() {
var ss = SpreadsheetApp.getActive();
var sourceValues = ss.getSheetByName('Items').getRange('A3:D15').getValues();
var target = ss.getSheetByName('Ingredients');
var result = [];
for (var i = 0; i < sourceValues[0].length; i++) {
for (var j = 0; j < sourceValues.length; j++) {
result.push([sourceValues[j][i]]);
}
}
target.getRange(1, 1, result.length, 1).setValues(result);
}
And that being said, you could use a bit of a workaround to provide the end result using spreadsheet functions only:
=ArrayFormula(UNIQUE(TRANSPOSE(SPLIT(CONCATENATE('Items'!A3:D15&CHAR(9));CHAR(9)))))

Related

How to find an empty cell and paste data in that one [duplicate]

I've made a script that every few hours adds a new row to a Google Apps spreadsheet.
This is the function I've made to find the first empty row:
function getFirstEmptyRow() {
var spr = SpreadsheetApp.getActiveSpreadsheet();
var cell = spr.getRange('a1');
var ct = 0;
while ( cell.offset(ct, 0).getValue() != "" ) {
ct++;
}
return (ct);
}
It works fine, but when reaching about 100 rows, it gets really slow, even ten seconds.
I'm worried that when reaching thousands of rows, it will be too slow, maybe going in timeout or worse.
Is there a better way?
This question has now had more than 12K views - so it's time for an update, as the performance characteristics of New Sheets are different than when Serge ran his initial tests.
Good news: performance is much better across the board!
Fastest:
As in the first test, reading the sheet's data just once, then operating on the array, gave a huge performance benefit. Interestingly, Don's original function performed much better than the modified version that Serge tested. (It appears that while is faster than for, which isn't logical.)
The average execution time on the sample data is just 38ms, down from the previous 168ms.
// Don's array approach - checks first column only
// With added stopping condition & correct result.
// From answer https://stackoverflow.com/a/9102463/1677912
function getFirstEmptyRowByColumnArray() {
var spr = SpreadsheetApp.getActiveSpreadsheet();
var column = spr.getRange('A:A');
var values = column.getValues(); // get all data in one call
var ct = 0;
while ( values[ct] && values[ct][0] != "" ) {
ct++;
}
return (ct+1);
}
Test results:
Here are the results, summarized over 50 iterations in a spreadsheet with 100 rows x 3 columns (filled with Serge's test function).
The function names match the code in the script below.
"First empty row"
The original ask was to find the first empty row. None of the previous scripts actually deliver on that. Many check just one column, which means that they can give false positive results. Others only find the first row that follows all data, meaning that empty rows in non-contiguous data get missed.
Here's a function that does meet the spec. It was included in the tests, and while slower than the lightning-fast single-column checker, it came in at a respectable 68ms, a 50% premium for a correct answer!
/**
* Mogsdad's "whole row" checker.
*/
function getFirstEmptyRowWholeRow() {
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getDataRange();
var values = range.getValues();
var row = 0;
for (var row=0; row<values.length; row++) {
if (!values[row].join("")) break;
}
return (row+1);
}
Complete script:
If you want to repeat the tests, or add your own function to the mix as a comparison, just take the whole script and use it in a spreadsheet.
/**
* Set up a menu option for ease of use.
*/
function onOpen() {
var menuEntries = [ {name: "Fill sheet", functionName: "fillSheet"},
{name: "test getFirstEmptyRow", functionName: "testTime"}
];
var sh = SpreadsheetApp.getActiveSpreadsheet();
sh.addMenu("run tests",menuEntries);
}
/**
* Test an array of functions, timing execution of each over multiple iterations.
* Produce stats from the collected data, and present in a "Results" sheet.
*/
function testTime() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
ss.getSheets()[0].activate();
var iterations = parseInt(Browser.inputBox("Enter # of iterations, min 2:")) || 2;
var functions = ["getFirstEmptyRowByOffset", "getFirstEmptyRowByColumnArray", "getFirstEmptyRowByCell","getFirstEmptyRowUsingArray", "getFirstEmptyRowWholeRow"]
var results = [["Iteration"].concat(functions)];
for (var i=1; i<=iterations; i++) {
var row = [i];
for (var fn=0; fn<functions.length; fn++) {
var starttime = new Date().getTime();
eval(functions[fn]+"()");
var endtime = new Date().getTime();
row.push(endtime-starttime);
}
results.push(row);
}
Browser.msgBox('Test complete - see Results sheet');
var resultSheet = SpreadsheetApp.getActive().getSheetByName("Results");
if (!resultSheet) {
resultSheet = SpreadsheetApp.getActive().insertSheet("Results");
}
else {
resultSheet.activate();
resultSheet.clearContents();
}
resultSheet.getRange(1, 1, results.length, results[0].length).setValues(results);
// Add statistical calculations
var row = results.length+1;
var rangeA1 = "B2:B"+results.length;
resultSheet.getRange(row, 1, 3, 1).setValues([["Avg"],["Stddev"],["Trimmed\nMean"]]);
var formulas = resultSheet.getRange(row, 2, 3, 1);
formulas.setFormulas(
[[ "=AVERAGE("+rangeA1+")" ],
[ "=STDEV("+rangeA1+")" ],
[ "=AVERAGEIFS("+rangeA1+","+rangeA1+',"<"&B$'+row+"+3*B$"+(row+1)+","+rangeA1+',">"&B$'+row+"-3*B$"+(row+1)+")" ]]);
formulas.setNumberFormat("##########.");
for (var col=3; col<=results[0].length;col++) {
formulas.copyTo(resultSheet.getRange(row, col))
}
// Format for readability
for (var col=1;col<=results[0].length;col++) {
resultSheet.autoResizeColumn(col)
}
}
// Omiod's original function. Checks first column only
// Modified to give correct result.
// question https://stackoverflow.com/questions/6882104
function getFirstEmptyRowByOffset() {
var spr = SpreadsheetApp.getActiveSpreadsheet();
var cell = spr.getRange('a1');
var ct = 0;
while ( cell.offset(ct, 0).getValue() != "" ) {
ct++;
}
return (ct+1);
}
// Don's array approach - checks first column only.
// With added stopping condition & correct result.
// From answer https://stackoverflow.com/a/9102463/1677912
function getFirstEmptyRowByColumnArray() {
var spr = SpreadsheetApp.getActiveSpreadsheet();
var column = spr.getRange('A:A');
var values = column.getValues(); // get all data in one call
var ct = 0;
while ( values[ct] && values[ct][0] != "" ) {
ct++;
}
return (ct+1);
}
// Serge's getFirstEmptyRow, adapted from Omiod's, but
// using getCell instead of offset. Checks first column only.
// Modified to give correct result.
// From answer https://stackoverflow.com/a/18319032/1677912
function getFirstEmptyRowByCell() {
var spr = SpreadsheetApp.getActiveSpreadsheet();
var ran = spr.getRange('A:A');
var arr = [];
for (var i=1; i<=ran.getLastRow(); i++){
if(!ran.getCell(i,1).getValue()){
break;
}
}
return i;
}
// Serges's adaptation of Don's array answer. Checks first column only.
// Modified to give correct result.
// From answer https://stackoverflow.com/a/18319032/1677912
function getFirstEmptyRowUsingArray() {
var sh = SpreadsheetApp.getActiveSpreadsheet();
var ss = sh.getActiveSheet();
var data = ss.getDataRange().getValues();
for(var n=0; n<data.length ; n++){
if(data[n][0]==''){n++;break}
}
return n+1;
}
/**
* Mogsdad's "whole row" checker.
*/
function getFirstEmptyRowWholeRow() {
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getDataRange();
var values = range.getValues();
var row = 0;
for (var row=0; row<values.length; row++) {
if (!values[row].join("")) break;
}
return (row+1);
}
function fillSheet(){
var sh = SpreadsheetApp.getActiveSpreadsheet();
var ss = sh.getActiveSheet();
for(var r=1;r<1000;++r){
ss.appendRow(['filling values',r,'not important']);
}
}
// Function to test the value returned by each contender.
// Use fillSheet() first, then blank out random rows and
// compare results in debugger.
function compareResults() {
var a = getFirstEmptyRowByOffset(),
b = getFirstEmptyRowByColumnArray(),
c = getFirstEmptyRowByCell(),
d = getFirstEmptyRowUsingArray(),
e = getFirstEmptyRowWholeRow(),
f = getFirstEmptyRowWholeRow2();
debugger;
}
The Google Apps Script blog had a post on optimizing spreadsheet operations that talked about batching reads and writes that could really speed things up. I tried your code on a spreadsheet with 100 rows, and it took about seven seconds. By using Range.getValues(), the batch version takes one second.
function getFirstEmptyRow() {
var spr = SpreadsheetApp.getActiveSpreadsheet();
var column = spr.getRange('A:A');
var values = column.getValues(); // get all data in one call
var ct = 0;
while ( values[ct][0] != "" ) {
ct++;
}
return (ct);
}
If the spreadsheet gets big enough, you might need to grab the data in chunks of 100 or 1000 rows instead of grabbing the entire column.
It's already there as the getLastRow method on the Sheet.
var firstEmptyRow = SpreadsheetApp.getActiveSpreadsheet().getLastRow() + 1;
Ref https://developers.google.com/apps-script/class_sheet#getLastRow
Seeing this old post with 5k views I first checked the 'best answer' and was quite surprised by its content... this was a very slow process indeed ! then I felt better when I saw Don Kirkby's answer, the array approach is indeed much more efficient !
But how much more efficient ?
So I wrote this little test code on a spreadsheet with 1000 rows and here are the results : (not bad !... no need to tell which one is which...)
and here is the code I used :
function onOpen() {
var menuEntries = [ {name: "test method 1", functionName: "getFirstEmptyRow"},
{name: "test method 2 (array)", functionName: "getFirstEmptyRowUsingArray"}
];
var sh = SpreadsheetApp.getActiveSpreadsheet();
sh.addMenu("run tests",menuEntries);
}
function getFirstEmptyRow() {
var time = new Date().getTime();
var spr = SpreadsheetApp.getActiveSpreadsheet();
var ran = spr.getRange('A:A');
for (var i= ran.getLastRow(); i>0; i--){
if(ran.getCell(i,1).getValue()){
break;
}
}
Browser.msgBox('lastRow = '+Number(i+1)+' duration = '+Number(new Date().getTime()-time)+' mS');
}
function getFirstEmptyRowUsingArray() {
var time = new Date().getTime();
var sh = SpreadsheetApp.getActiveSpreadsheet();
var ss = sh.getActiveSheet();
var data = ss.getDataRange().getValues();
for(var n =data.length ; n<0 ; n--){
if(data[n][0]!=''){n++;break}
}
Browser.msgBox('lastRow = '+n+' duration = '+Number(new Date().getTime()-time)+' mS');
}
function fillSheet(){
var sh = SpreadsheetApp.getActiveSpreadsheet();
var ss = sh.getActiveSheet();
for(var r=1;r<1000;++r){
ss.appendRow(['filling values',r,'not important']);
}
}
And the test spreadsheet to try it yourself :-)
EDIT :
Following Mogsdad's comment, I should mention that these function names are indeed a bad choice... It should have been something like getLastNonEmptyCellInColumnAWithPlentyOfSpaceBelow() which is not very elegant (is it ?) but more accurate and coherent with what it actually returns.
Comment :
Anyway, my point was to show the speed of execution of both approaches, and it obviously did it (didn't it ? ;-)
I know this is an old thread and there have been some very clever approaches here.
I use the script
var firstEmptyRow = SpreadsheetApp.getActiveSpreadsheet().getLastRow() + 1;
if I need the first completely empty row.
If I need the first empty cell in a column I do the following.
My first row is usually a title row.
My 2nd row is a hidden row and each cell has the formula
=COUNTA(A3:A)
Where A is replaced with the column letter.
My script just reads this value. This updates pretty quickly compared to script approaches.
There is one time this does not work and that is when I allow empty cells to break up the column. I have not needed a fix for this yet, I suspect one may be derived from COUNTIF, or a combined function or one of the many other inbuilt ones.
EDIT: COUNTA does cope with blank cells within a range, so the concern about the "one time this does not work" is not really a concern. (This might be a new behavior with "new Sheets".)
And why don't use appendRow?
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
spreadsheet.appendRow(['this is in column A', 'column B']);
I have a similar issue. Right now it's a table with many hundreds of rows, and I'm expecting it to grow to many many thousands. (I haven't seen whether a Google spreadsheet will handle tens of thousands of rows, but I'll get there eventually.)
Here's what I'm doing.
Step forward through the column by hundreds, stop when I'm on an empty row.
Step backward through the column by tens, looking for the first non-empty row.
Step forward through the column by ones, looking for the first empty row.
Return the result.
This depends of course on having contiguous content. Can't have any random blank lines in there. Or at least, if you do, results will be sub-optimal. And you can tune the increments if you think it's important. These work for me, and I find that the difference in duration between steps of 50 and steps of 100 are negligible.
function lastValueRow() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var r = ss.getRange('A1:A');
// Step forwards by hundreds
for (var i = 0; r.getCell(i,1).getValue() > 1; i += 100) { }
// Step backwards by tens
for ( ; r.getCell(i,1).getValue() > 1; i -= 10) { }
// Step forwards by ones
for ( ; r.getCell(i,1).getValue() == 0; i--) { }
return i;
}
This is much faster than inspecting every cell from the top. And if you happen to have some other columns that extend your worksheet, it may be faster than inspecting every cell from the bottom, too.
I tweaked the code ghoti supplied so that it searched for an empty cell. Comparing values did not work on a column with text (or I could not figure out how) instead I used isBlank(). Notice the value is negated with ! (in front of the variable r) when looking forward since you want i to increase until a blank is found. Working up the sheet by ten you want to stop decreasing i when you find a cell that is not blank (! removed). Then, back down the sheet by one to the first blank.
function findRow_() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
ss.setActiveSheet(ss.getSheetByName("DAT Tracking"));
var r = ss.getRange('C:C');
// Step forwards by hundreds
for (var i = 2; !r.getCell(i,1).isBlank(); i += 100) { }
// Step backwards by tens
for ( ; r.getCell(i,1).isBlank(); i -= 10) { }
// Step forwards by ones
for ( ; !r.getCell(i,1).isBlank(); i++) { }
return i;
Just my two cents, but I do this all the time. I just write the data to the TOP of the sheet. It's date reversed (latest on top), but I can still get it to do what I want. The code below has been storing data it scrapes from a realtor's site for the past three years.
var theSheet = SpreadsheetApp.openById(zSheetId).getSheetByName('Sheet1');
theSheet.insertRowBefore(1).getRange("A2:L2").setValues( [ zPriceData ] );
This chunk of the scraper function inserts a row above #2 and writes the data there. The first row is the header, so I don't touch that. I haven't timed it, but the only time I have an issue is when the site changes.
Indeed the getValues is a good option but you can use the .length function to get the last row.
function getFirstEmptyRow() {
var spr = SpreadsheetApp.getActiveSpreadsheet();
var array = spr.getDataRange().getValues();
ct = array.length + 1
return (ct);
}
Using indexOf is one of the ways to achieve this:
function firstEmptyRow() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getActiveSheet();
var rangevalues = sh.getRange(1,1,sh.getLastRow(),1).getValues(); // Column A:A is taken
var dat = rangevalues.reduce(function (a,b){ return a.concat(b)},[]); //
2D array is reduced to 1D//
// Array.prototype.push.apply might be faster, but unable to get it to work//
var fner = 1+dat.indexOf('');//Get indexOf First empty row
return(fner);
}
I have gone through way too many of these implementations of last-row for a specific column. Many solutions work but are slow for large or multiple datasets. One of my use cases requires me to check the last row in specific columns across multiple spreadsheets. What I have found is that taking the whole column as a range and then iterating through it is too slow, and adding a few of these together makes the script sluggish.
My "hack" has been this formula:
=ROW(index(sheet!A2:A,max(row(sheet!A2:A)*(sheet!A2:A<>""))))-1
Example: Add this to Cell A1, to find the last row in column A. Can be added anywhere, just make sure to manage the "-1" at the end depending on which row the formula is placed. You can also place this is another col, rather than the one you're trying to count, and you don't need to manage the -1. You could also count FROM a starting Row, like "C16:C" - will count values C16 onwards
This formula is reliably giving me the last row, including blanks in the middle of the dataset
To use this value in my GS code, I am simply reading the cell value from A1. I understand that Google is clear that spreadsheet functions like read/write are heavy (time-consuming), but this is much faster than column count last-row methods in my experience (for large datasets)
To make this efficient, I am getting the last row in a col once, then saving it as a global variable and incrementing in my code to track which rows I should be updating. Reading the cell every-time your loop needs to make an update will be too inefficient. Read once, iterate the value, and the A1 cell formula (above) is "storing" the updated value for the next time your function runs
This also works if the data has filters turned on. Actual last row is maintained
Please let me know if this was helpful to you! If I encounter any issues I will comment on this answer.
combo of DON and Ghoti.
function getLastRowNumber(sheet, columnLabel) {
var columnLabel = sheet.getRange(`${columnLabel}:${columnLabel}`);
var values = columnLabel.getValues(); // get all data in one call
var ct = 0;
for (; values.length > ct && values[ct][0] != ""; ct += 100);
// Step backwards by tens
for ( ; ct > 0 && values[ct][0] == ""; ct -= 10);
// Step forwards by ones
for ( ; values.length > ct && values[ct][0] != ""; ct ++);
return ct;
}
I keep an extra "maintenance" sheet, on my spreadsheets, where I keep such data.
To get the next free row of a range I just examine the relevant cell. I can get the value instantly, because the work of finding the value happens when the data is changed.
The formula in the cell is usually something like :
=QUERY(someSheet!A10:H5010,
"select min(A) where A > " & A9 & " and B is null and D is null and H < 1")
The value in A9 can be set periodically to some row that is near "enough" to the end.
Caveat : I have never checked if this is viable for huge data sets.
Finally I got a single line solution for it.
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var lastEmptyOnColumnB = sheet.getRange("B1:B"+sheet.getLastRow()).getValues().join(",").replace(/,,/g, '').split(",").length;
It works fine for me.
Here is a list of what the code should do:
Give a correct answer if there are no empty cells
Be fast
Return the correct row number - not the index number of the array
Get the correct row number of the empty cell even when other columns in the sheet tab have more rows with data
Have good variable names
Answer the original question
Avoid unnecessary data processing
Provide comment explanations for what the code does
Be generic enough to adapt to the readers conditions
This solution uses the array method some which will stop iterating the loop when the condition is true. This avoids wasting time spent looping through every element of the array, and yet uses an array method rather than a for or while loop.
The some method only returns true or false, but there is a way to capture the index number because the some method halts looping when the condition is true.
The index number is assigned to a variable in the scope outside of the array function. This does not slow down the processing.
Code:
function getFirstEmptyCellIn_A_Column(po) {
var foundEmptyCell,rng,sh,ss,values,x;
/*
po.sheetTabName - The name of the sheet tab to get
po.ssID - the file ID of the spreadsheet
po.getActive - boolean - true - get the active spreadsheet -
*/
/* Ive tested the code for speed using many different ways to do this and using array.some
is the fastest way - when array.some finds the first true statement it stops iterating -
*/
if (po.getActive || ! po.ssID) {
ss = SpreadsheetApp.getActiveSpreadsheet();
} else {
ss = SpreadsheetApp.openById(po.ssID);
}
sh = ss.getSheetByName(po.sheetTabName);
rng = sh.getRange('A:A');//This is the fastest - Its faster than getting the last row and getting a
//specific range that goes only to the last row
values = rng.getValues(); // get all the data in the column - This is a 2D array
x = 0;//Set counter to zero - this is outside of the scope of the array function but still accessible to it
foundEmptyCell = values.some(function(e,i){
//Logger.log(i)
//Logger.log(e[0])
//Logger.log(e[0] == "")
x = i;//Set the value every time - its faster than first testing for a reason to set the value
return e[0] == "";//The first time that this is true it stops looping
});
//Logger.log('x + 1: ' + (x + 1))//x is the index of the value in the array - which is one less than the row number
//Logger.log('foundEmptyCell: ' + foundEmptyCell)
return foundEmptyCell ? x + 1 : false;
}
function testMycode() {
getFirstEmptyCellIn_A_Column({"sheetTabName":"Put Sheet tab name here","ssID":"Put your ss file ID here"})
}
this is my very first post on stackOverflow, I hope to meet all your netiquette needs, so please be nice to me.
considerations
I think the fastest way to find the first blank cell in a column (I couldn't run the performance checks, anyway) is to let the Google engine do sequential tasks itself; it is simply much more efficient. From a programmer's point of view, this translates into NOT using any kind of iteration/loops, i.e. FOR, WHILE, etc. (By the way, this is the same programming approach on database engines - any activity should NOT use loops to find information.)
the idea
Go all way DOWN and find the cell in last row of the Sheet (considering all columns),
from there, go UP find the first cell containing data in the specified column (selecting the column),
shift down one cell to find a free place.
The following function does this in just one command (neglecting the var declarations, here just to improve readability):
code
function lastCell() {
var workSheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var lastRow = workSheet.getLastRow();
var columnToSearch = 1; //index of the column to search. 1 is 'A'.
workSheet.getRange(lastRow, columnToSearch).activateAsCurrentCell().
getNextDataCell(SpreadsheetApp.Direction.UP).activate();
workSheet.getCurrentCell().offset(1, 0).activate(); // shift one cell down to find a free cell
}

Google Sheets - How can I find the first row number containing specific text, and then clear/delete all content to end?

I'm currently importing a bunch of data and then splitting it into multiple columns and then attempting to clear or delete any erroneous rows (it's a raw data import that contains a bunch of scrap rows).
So far I've got the data imported, split, & sorted. Now I'm trying to find the row number, based on a value in column A, and then select all rows to the end of the sheet to either delete or clear the content.
I'm to the point where all of my data has been split into the columns I need (A:J) and sorted so that all relevant data is at the top (it's a variable data set) so now I'm just trying to find the first row that contains "----------------------" as this will be my first 'garbage' row.
outputrange.setValues(output);
pull1.deleteRows(1, 40);
pull1.getRange(2, 1, pull1.getLastRow()-1,
pull1.getLastColumn()).activate().sort({column:2, ascending: true});
var removalValues = range.getValues()
for (var j=0; j<removalValues.length; j++) {
var rowArray = removalValues[j];
for (var k=0; k<rowArray.length; k++) {
var columnValue = rowArray[k];
if (rowArray[0] === "----------------------") {
var rowNumber = i;
pull1.getRange(rowNumber, 1, 1, pull1.getLastColumn()).activate()
}
}
}
I've attempted the code above to loop through and find the correct cell reference, and just temporarily highlight the row so I make sure it's functioning correctly. Currently this part of my code processes but otherwise doesn't do anything. Really I just need something that will look through my data in column A and find the matching data, then return the row number for me so that I can apply it to other formulas.
Edit: I updated my code using some additional resources and came up with the following. It seems to work correctly but I'm not sure if it's the most efficient solution:
var outputrange = pull1.getRange(startRow, 1, LR-startRow+1, 10)
outputrange.setValues(output);
pull1.deleteRows(1, 40);
pull1.getRange(2, 1, pull1.getLastRow()-1,
pull1.getLastColumn()).activate().sort({column:2, ascending: true});
var rangeData = pull1.getDataRange();
var lastColumn = rangeData.getLastColumn();
var lastRow = rangeData.getLastRow();
var searchRange = pull1.getRange(1,1,lastRow-1,lastColumn-1);
var removalValues = searchRange.getValues();
for (j=0; j < lastColumn-1; j++) {
for (k=0; k < lastRow-1; k++) {
if(removalValues[k][j] === "----------------------") {
pull1.getRange(k+1, 1, pull1.getLastRow(), 10).deleteCells(SpreadsheetApp.Dimension.ROWS);
}
}
}
Requirement:
Find specified text then delete all rows below said text.
Solution:
Use textFinder to find the text then pass the result row to deleteRows().
function findAndClear() {
var sh = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var lastRow = sh.getLastRow() + 1;
var range = sh.getRange(1, 1, lastRow);
//find specified text and return row number
var row = range.createTextFinder('----------------------').findNext().getRow();
//calculate number of rows to delete
var delRows = lastRow - row;
//delete rows
sh.deleteRows(row, delRows);
}
Explanation:
First of all, sorry but I've scrapped the script you were using. There's no need to get all of the values in the column then loop through each one, it's over-complicated and slow.
Using textFinder is much quicker, no looping through arrays, just finding the first occurrence of your string in column A and getting the row number.
Notes:
If you did want to keep the row containing your string, use:
sh.deleteRows(row + 1, delRows);
References:
textFinder Documentation

Generating a static unique ID that isn't linked to the row number

I'm pretty terrible at coding, but have been trying to piece together a script to generate a static unique ID for placement within a google sheet.
What I would like to to happen is when any data is entered in column D, the equivalent row in column A generates a unique ID that is incremental from the last ID. However the unique ID must be static in that is not linked to the row number in any way. The unique ID needs also needs to increment in the order that data is entered in column D, which won't necessarily be entered in sequence with row numbers (i.e. ID2 may be in row 16, with ID3 in row 6).
I've located and adapted two scripts which I feel are nearly achieving what needs to be done, but have reached a limit of my understanding to finalise. Can anyone help explain how I would adapt the OnEdit function to include the setValue command from the OnForm function please?
function OnForm(){
var sh = SpreadsheetApp.getActiveSheet()
var startcell = sh.getRange('A1').getValue();
if(! startcell){sh.getRange('A1').setValue(1);return};
var colValues = sh.getRange('A1:A').getValues();
var max=0;// define the max variable to a minimal value
for(var r in colValues){ // iterate the array
var vv=colValues[r][0].toString().replace(/[^0-9]/g,'');
if(Number(vv)>max){max=vv};
}
max++ ; // increment to be 1 above max value
sh.getRange(sh.getLastRow()+1, 1).setValue(Utilities.formatString('IG%05d',max));
}
function onEdit(e) {
var ss = e.source.getActiveSheet();
var watchedCols = [4]
if (watchedCols.indexOf(e.range.columnStart) === -1) return;
ss.getRange(e.range.rowStart, 2)
.setValue('unique ID here')
}
If I understood your problem correctly, you should loop through the values in the ID column and search for the biggest value. After looping through the column and having the biggest value stored in a variable, just increment that value by one and set it as the new ID.
So run this loop before you set the value of the cell:
var drang = ss.getDataRange();
var ids = ss.getRange(1, 2, drang.getLastRow(), 1).getValues();
var biggest = 0;
for(var i = 0; i < ids.length; i++){
var id = ids[i][0];
if(id > biggest){
biggest = id;
}
}
And then when you set the value:
ss.getRange(e.range.rowStart, 2)
.setValue(biggest+1)

Google App Script Catch Row Number

I have looked everywhere and seen the related questions on this board, but I am really stuck.
I need to capture the actual row value of a Sheet at is iterates through a for loop based on an if statement.
In other parts of the code, I have to create a copy of a Form's response sheet and move it to a folder. I am trying to log the file id of the new file that is being created to a sheet that already exists (and in which the scripts are being called from a menu).
I can get the data and the values and iterate through them and see everything in the Logs. And based on what I saw in other similar questions and such, I tried the getActiveRange().getRow() part - but it only works for the exact row that the cursor is on when the form is open. I need to be able to dynamically set the ActiveRow variable based on the row that the script is currently looking at as it goes through the for loop.
I can not use the for loop variable as my current row (j+1), because the if statement is only looking for a string value "NOT FOUND" in the column - so the "j" variable value would not necessarily match the row value to update.
Thank you so much for any assistance!!
// Get the active spreadsheet and the active sheet
var ssEXD = SpreadsheetApp.getActive().getSheetByName('FormData');
var lastRowEXD = ssEXD.getLastRow();
var lastColumnEXD = ssEXD.getLastColumn();
var formIDValues = ssEXD.getSheetValues(2, 1, lastRowEXD-1, lastColumnEXD);
//Loop through the returned values
for(var j = 0; j < formIDValues.length; j++) {
//if NOT FOUND is true, do stuff
if (formIDValues[j][6] == "NOT_FOUND") {
//need this variable to be the exact row of the sheet value data
var ActiveRow = ssEXD.getActiveRange().getRow();
//I need to use the ActiveRow in the getActiveRange to update the sheet with some information.
ssEXD.getRange(ActiveRow,6,1).setValue("testing");
}
}
If I'm reading your question correctly your data has a column names on the first row and data begins on the second row.
So I think this will accomplish what you want.
var ssEXD = SpreadsheetApp.getActive().getSheetByName('FormData');
var lastRowEXD = ssEXD.getLastRow();
var lastColumnEXD = ssEXD.getLastColumn();
var dataR = ssEXD.getDataRange();
var dataA = dataR.getValues();
for(var j = 1; j < dataA.length; j++)
{
if (dataA[j][6] == "NOT_FOUND")
{
dataA[j][6] = "testing";
}
}
dataR.setValues(dataA);
var ActiveRow = j + 2;
Once an array(formIDValues) has been extracted from sheet, the array has no more relation to that sheet. One must adjust the index of the item being worked on in the array to the row number in the spreadsheet.
j - the array index - starts at zero which is the second row in this spreadsheet, therefore j + 2
Your concern about the j variable is unwarranted. j is incremented for every pass through the loop whether or not any test has been passed.

Find string and get its column

Let's say I have a lot of columns and one of them contains "impressions" string (on row 3). What I need to do is to:
1) Find the cell with "impressions" string
2) Get column number or i.e. "D"
3) Based on what I got paste a formula into i.e. D2 cell which gets AVERAGE from a range D4:D*last*
I couldn't find it anywhere so I have to ask here without any "sample" code, since I have no idea on how to achieve what I want. (3rd one is easy but I need to get that "D" first)
There's no way to search in Google Apps Script. Below is a function that will accomplish the first 2 parts for you (by iterating over every cell in row 3 and looking for "impressions"):
function findColumnNumber() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Sheet1'); // insert name of sheet here
var range = sheet.getDataRange(); // get the range representing the whole sheet
var width = range.getWidth();
// search every cell in row 3 from A3 to the last column
for (var i = 1; i <= width; i++) {
var data = range.getCell(3,i)
if (data == "impressions") {
return(i); // return the column number if we find it
}
}
return(-1); // return -1 if it doesn't exist
}
Hopefully this will allow you to accomplish what you need to do!
The indexOf method allows one to search for strings:
function findColumnNumber() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet() //whatever tab the code is run on
var data = sheet.getDataRange().getValues();
var header_row_num = 1; // TODO: change this to whichever row has the headers.
var header = data[header_row_num -1] //Remember JavaScript, like most programming languages starts counting (is indexed) at 0. For the value of header_row_num to work with a zero-index counting language like JavaScript, you need to subtract 1
//define the string you want to search for
var searchString = "impressions";
//find that string in the header and add 1 (since indexes start at zero)
var colNum = header.indexOf(searchString) + 1;
return(colNum);