I'm trying to run an IF function to match the date in the first column to "last month" and the date in the last column to "newest date" and copy and paste all of the rows matching this criteria (excluding the first and last column) to the bottom of the list.
This is the script I'm running and it isn't finding any matches when I know for a fact there are at least 100 rows matching this criteria:
function myFunction() {
var MCS = SpreadsheetApp.openById('[ID REMOVED FOR THIS Q]');
var MRB = MCS.getSheetByName('Media Rates Back');
var MRBrange = MRB.getRange(1,1,MRB.getLastRow(),1).getValues();
var dest = MRBrange.filter(String).length + 1;
var LM = new Date();
LM.setDate(1);
LM.setMonth(LM.getMonth()-1);
var LMs = Date.parse(LM);
var Datenew = MRB.getRange(MRB.getLastRow(),MRB.getLastColumn()).getValue();
var Datecol = MRB.getRange(1,6,MRB.getLastRow(),1).getValues();
var Datenews = Date.parse(Datenew);
for(var i=0; i<MRBrange.length; i++) {
if(Date.parse(MRBrange[i])==LMs && Date.parse(Datecol[i])==Datenews ) {
var NewRange = MRB.getRange(i,2,(MRB.getLastRow()-i),5);
var NewRangeV = NewRange.getValues();
var destination = MRB.getRange(MRB.getLastRow()+1,2);
Logger.log(NewRange);
NewRange.copyTo(destination);
}else{
Logger.log(i);
}
}}
Any help would be appreciated!
Rather than get the columns as separate ranges, I would get the entire range as one array, then loop over that and check the two columns.
I'm also assuming your values are formatted as dates in the Sheet, in which case you don't need to use Date.parse(), and that your actual date logic is correct.
You can try using the debugger and set a breakpoint at the IF, so you can check the values it is comparing. or put a Logger.log call to list your comparisons.
var last_month_column = 1;
var newest_date_column = MRB.getLastColumn();
var MRBrange = MRB.getRange(1,1,MRB.getLastRow(),newest_date_column).getValues();
for(var row in MRBrange) {
if(MRBrange[row][last_month_column]==LMs && Datecol[row][newest_date_column] ==Datenews ) {
/* your copy logic here */
}else{
Logger.log(i);
}
}
I think the problem may be that MRBrange is a 2d Array. So I used another loop to convert it to a 1d array.
function myFunction() {
var MCS = SpreadsheetApp.openById('[ID REMOVED FOR THIS Q]');
var MRB = MCS.getSheetByName('Media Rates Back');
var MRBrangeA = MRB.getRange(1,1,MRB.getLastRow(),1).getValues();//2d array
var MRBrange=[];
for(var i=0;i<MRBrangeA.length;i++)
{
MRBrange.push(MRBrangA[i][0]);//1d array
}
var dest = MRBrange.filter(String).length + 1;
var LM = new Date();//current day
LM.setDate(1);//first day of month
LM.setMonth(LM.getMonth()-1);//first day of last month
var LMs = Date.parse(LM);
var Datenew = MRB.getRange(MRB.getLastRow(),MRB.getLastColumn()).getValue();
var Datecol = MRB.getRange(1,6,MRB.getLastRow(),1).getValues();
var Datenews = Date.parse(Datenew);
for(var i=0; i<MRBrange.length; i++) {
if(Date.parse(MRBrange[i])==LMs && Date.parse(Datecol[i])==Datenews ) {
var NewRange = MRB.getRange(i,2,(MRB.getLastRow()-i),5);
var NewRangeV = NewRange.getValues();
var destination = MRB.getRange(MRB.getLastRow()+1,2);
Logger.log(NewRange);
NewRange.copyTo(destination);
}else{
Logger.log(i);
}
}}
Related
I am making an add on that processes content from a Google Sheets doc and returns it (for now) to new sheet in the spreadsheet. The first iteration works but is deadly slow. I think it's because it processes the data sheet one line at a time. So I'm rewriting it to ingest all the data and then process the array it lives in.
Problem is the amount of data appears to be prohibitive. I can see this in Logger output, in execution transcript, and in stackdriver logging; it gets to about line 35 (11 columns) before it runs out of room.
here is the original version (takes an insane amount of time):
'''
var compareDate = selDate;
var bulletin = ss.getSheetByName(reportSheetName);
var responses = ss.getSheetByName("Calendar");
var lastRow = responses.getLastRow(); //get last row of data sheet
var lastBull = bulletin.getLastRow(); //get last row of bulletin sheet
var nextBullRow = lastBull+1;
var nextBullItem = bulletin.getRange(nextBullRow,1);
for(var i = 2; i <= lastRow; i++) {
var row = responses.getRange(i, 1, 1, 11).getValues();
var dateA = new Date((row[0][4]).valueOf());
var eventType = row[0][0].valueOf();
var eventCalDate = new Date(row[0][4].valueOf());
var eventEvent = row[0][3].valueOf();
var eventOpp = row[0][7].valueOf();
var eventLocation = row[0][8].valueOf();
var eventMonth = eventCalDate.getMonth();
var eventDate = eventCalDate.getDate();
var eventYear = eventCalDate.getYear();
var eventShortDate = eventMonth+"/"+eventDate+"/"+eventYear;
var start = new Date(thisWeekSt);
var end = new Date(thisWeekEnd);
/*var masterDate = new Date(compareDate).valueOf();
var thisCat = row[0][3];
var bullItem = row[0][4]; */
var dateMatch = false;
if(dateA > start && dateA < end) {
dateMatch=true;
bulletin.getRange(nextBullRow,1,nextBullRow,6).setFontWeight("normal");
bulletin.getRange(nextBullRow,typeCol).setValue(eventType);
bulletin.getRange(nextBullRow,dateCol).setValue(eventShortDate);
bulletin.getRange(nextBullRow,timeCol).setValue(eventType);
bulletin.getRange(nextBullRow,eventCol).setValue(eventEvent);
bulletin.getRange(nextBullRow,oppCol).setValue(eventOpp);
bulletin.getRange(nextBullRow,locCol).setValue(eventLocation);
nextBullRow++;
Logger.log(bulletin.getRange(nextBullRow-1,6).getValue());
if(bulletin.getRange(nextBullRow-1,6).getValue()=="Hollister") {
bulletin.getRange(nextBullRow-1,1,nextBullRow-1,6).setFontWeight("bold");
Logger.log("boop!");
}
'''
And here's the new version, which doesn't have the digital appetite for all the data;
'''
var compareDate = selDate;
var bulletin = ss.getSheetByName(reportSheetName);
var responses = ss.getSheetByName("Calendar");
var lastRow = responses.getLastRow(); //get last row of data sheet
var lastBull = bulletin.getLastRow(); //get last row of bulletin sheet
var nextBullRow = lastBull+1;
var nextBullItem = bulletin.getRange(nextBullRow,1);
var allData = responses.getRange(2, 1, responses.getLastRow(), 11).getValues();
for(var i = 2; i <= allData.getLastRow(); i++) {
var row = allData.getRange(i, 1, 1, 11).getValues();
Logger.log(row);
'''
Let me know if you want the whole function, I'll edit.
The question is: how can I speed data processing from the sluggish pace it is at currently. (Thanks Cooper!)
In your for loop you are calling getValues each iteration of your loop which looks like the issue. You've already got the data in your call above.
Try:
for(var i = 0; i <= allData.getLastRow(); i++) {
var row = allData[i];
Logger.log(row);
Had the same issue today. My solution was to duplicate the sheet and then work on the copy:
sheet=SpreadsheetApp.getActiveSpreadsheet()
copy=sheet.duplicateActiveSheet()
after duplicating, the copy is the active sheet, so you can directly work on it, e.g. delete rows like that:
sheet.deleteRows(1,2)
I have a script that runs in a google sheet that parses emails and creates new lines in the sheet. This is used to create a log file from periodically emailed log updates. This works very well.
Currently, I have a variable that is used to determine which emails are ingested based on the month (0=January, etc.)
That variable has to be adjusted every month and then I have to create a new monthly sheet (tab in the main) and do a bunch of sorting and moving emails in gmail.
I'd like to set this up so it automatically puts the January emails in a sheet for January and the February emails in a sheet for February.
I thought about cascading if elseif statements, but that got too unwieldy fast.
I thought about iterating using a for loop through an array holding all emails, but that seems convoluted too.
Any suggestions?
::EDIT::
To be clear, I'm really interested in how to parse all of the emails and send the ones from January to the January sheet (for example).
::EDIT:: Added current script
function myFunction() {
var label = GmailApp.getUserLabelByName(myLabel);
var label2 = GmailApp.getUserLabelByName(newLabel);
var threads = label.getThreads();
var data = new Array();
var newData = new Array();
// get all the email threads matching myLabel
for (var i = 0; i < threads.length; i++) {
var messages = GmailApp.getMessagesForThread(threads[i]);
// archive thread
label2.addToThread(threads[i]);
label.removeFromThread(threads[i]);
// get each individual email from the threads
for (var j = 0; j < messages.length; j++) {
var bodyText = messages[j].getPlainBody();
// split the email body into individual "paragraph" strings based on the regExp variable
while (matches = regExp.exec(bodyText)) {
var logdata = matches[1];
for (k in keys) {
logdata = logdata.replace(keys[k], "");
}
// split out each "paragraph" string into an array
var lines = logdata.split(/[\r\n]+/);
for (l in lines) {
lines[l] = lines[l].replace('*F','');
lines[l] = lines[l].trim();
}
for (l in lines) {
lines[l] = lines[l].replace(/^(\:\s)/, "");
}
// Turn the first element in the array into a date element, format it, and put it back
lines[0] = Utilities.formatDate(new Date(lines[0]), "America/Phoenix", "M/d/yy HH:mm:ss");
// Put the array to a new item in the data array for further processing
if (curMonth == (new Date(lines[0]).getMonth())) {
data.push(lines);
}
}
}
}
// Compare the information in the data array to oldData information in the sheet
if (data.length) {
var oldData = s.getRange(range).getValues();
for (h in oldData) {
oldData[h][0] = Utilities.formatDate(new Date(oldData[h][0]), "America/Phoenix", "M/d/yy HH:mm:ss");
}
for (i in data) {
var row = data[i];
var duplicate = false;
for (j in oldData) {
if (row.join() == oldData[j].join()) {
duplicate = true;
}
}
if (!duplicate) {
newData.push(row);
}
}
// check to write newData only if there is newData, this stops an error when newData is empty
if (newData.length) {
s.getRange(s.getLastRow() + 1, 1, newData.length, newData[0].length).setValues(newData);
}
s.getRange(range).sort(1); //sorts the sheet
}
}
Try this:
function getSheet(date) {
var ss=SpreadsheetApp.openById('SpreadsheetId');
var name=Utilities.formatDate(new Date(date), Session.getScriptTimeZone(), "MMMyyyy")
var sh=ss.getSheetByName(name);
if(!sh) {
var sh=ss.insertSheet(name);
}
return sh;
}
I ended up basing my solution on the solution that #cooper provided, but had to go a little further.
var ss = SpreadsheetApp.getActiveSpreadsheet();
var month = new Array("January","February","March","April","May","June","July","August","September","October","November","December");
var curMonth = new Date().getMonth(); //number of month -1 aka: January = 0
var sheetname = month[curMonth] + " " + new Date().getYear();
var s = ss.getSheetByName(sheetname);
function newMonth(){
if (!s) {
var template = ss.getSheetByName('Template').copyTo(ss);
template.setName(sheetname);
s = ss.getSheetByName(sheetname); //"reload" the sheet
s.showSheet(); //unhide the new copy since 'Template' is hidden in the spreadsheet
ss.setActiveSheet(s); //make it active
ss.moveActiveSheet(0); //move it to the first position
}
}
I currently have a list with two columns. The first column is student name, and the second column is the number of points they have.
I imported this list from multiple spreadsheets so there were many duplicates on the names of the students. I am able to remove the duplicates, but I want to keep a tally on the total points they have. For example:
Amy 10
Bob 9
Carol 15
Amy 12
would turn into:
Amy 22
Bob 9
Carol 15
This is what I have so far:
var target = SpreadsheetApp.getActiveSpreadsheet();
var sheet = target.getSheetByName("Sheet2");
var data = sheet.getRange("A2:B1000").getValues();
var newData = new Array();
var k = 0
var finallist = []
for(i in data){
k++;
var row = data[i];
var duplicate = false;
for(j in newData){
if(row[0] == newData[j][0]){
duplicate = true;
var storedHour = sheet.getRange("B"+k).getValue();
var position = finallist.indexOf(row[0]);
var originalCell = sheet.getRange("B"+(position+1));
var originalHour = originalCell.getValue();
originalCell.setValue(originalHour + storedHour);
sheet.getRange(k,2).setValue("")
sheet.getRange(k,1).setValue("")
}
}
if(!duplicate){
newData.push(row);
finallist.push(row[0])
}
}
}
The problem I'm having is that we have a really large data sample and I'm afraid it may run over Google's 5 minute maximum execution time. Is there another more efficient way to achieve my goal?
Your code is running slow because Spreadsheets API methods (like getRange) are time consuming and much slower then other JavaScript code.
Here is optimized function with reduced number of such Spreadsheets API calls:
function calcNumbers()
{
var target = SpreadsheetApp.getActiveSpreadsheet();
var sheet = target.getSheetByName("Sheet2");
var lastRow = sheet.getLastRow();
var dataRange = sheet.getRange(2, 1, lastRow-1, 2);
var data = dataRange.getValues();
var pointsByName = {};
for (var i = 0; i < data.length; i++)
{
var row = data[i];
var curName = row[0];
var curNumber = row[1];
// empty name
if (!curName.trim())
{
continue;
}
// if name found first time, save it to object
if (!pointsByName[curName])
{
pointsByName[curName] = Number(curNumber);
}
// if duplicate, sum numbers
else
{
pointsByName[curName] += curNumber;
}
}
// prepare data for output
var outputData = Object.keys(pointsByName).map(function(name){
return [name, pointsByName[name]];
});
// clear old data
dataRange.clearContent();
// write calculated data
var newDataRange = sheet.getRange(2, 1, outputData.length, 2);
newDataRange.setValues(outputData);
}
Sorting before comparing allows looking at the next item only instead of all items for each iteration. A spillover benefit is finallist result is alphabatized. Execution time reduction significant.
function sumDups() {
var target = SpreadsheetApp.getActiveSpreadsheet();
var sheet = target.getSheetByName("Sheet2");
var data = sheet.getRange("A2:B" + sheet.getLastRow()).getValues().sort();
var finallist = [];
for(var i = 0; i<= data.length - 1; i++){
var hours = data[i][1];
while((i < data.length - 1) && (data[i][0] == data[i+1][0])) {
hours += data[i+1][1];
i++;
};
finallist.push([data[i][0], hours]);
};
Logger.log(finallist);
}
Edit: the simple data structure with the name being in the first column allows this to work. For anything more complex understanding and applying the methods shown in #Kos's answer is preferable
I've just started using Google Apps script to manage some sheets for a project i'm working on, I am new to Javascript so please go easy if there are any howlers in my code!.
We have and app called forms2mobile that captures data and drops it into a Google spreadsheet. It actually drops different data into different sheets depending on which part of the app you use.
I've hacked together a script that pulls all data from one sheet (source), and drops only certain columns into a second sheet (destination). It then deletes all rows from the source, and any blank rows from the destination.
The problem I have is with deleting blank rows from the destination. Typically the destination will have empty rows at the bottom, and the code I have will only delete empty rows within the range that contains data. So i'm always left with empty rows at the bottom.
The destination sheet will then be used as a data source for forms2mobile, which of course isn't happy with empty rows.
I've found the class getMaxRows() but i'm not sure how to implement it. If anyone could make any suggestions that would be great.
Cheers
Paul
function NEW_copyColumnNumbers( ) {
var spreadsheet_source = SpreadsheetApp.openById('1a89ZIUcy-8168D1damCV3Q9Ix0arQn9jGS6pgp');
var spreadsheet_target = SpreadsheetApp.openById('1GQiLt9utSH_6CV__oJwmcLOkI4E9iNIRPWU7Xr');
var range_input = spreadsheet_source.getRange("A2:CC407");
var range_output = spreadsheet_target.getRange("A"+(spreadsheet_target.getLastRow()+1));
var keep_columns = [66,66,10,11,12,13,14,23,26,31,69,71,74,75,80];
copyColumnNumbers(range_input, range_output, keep_columns);
clearEmptyRows();
clearSourceData();
}
function copyColumnNumbers( range_input, range_output, columns_keep_num ) {
// Create an array of arrays containing the values in the input range.
var range_values = range_input.getValues();
// Loop through each inner array.
for ( var i = 0, row_count = range_values.length; i < row_count; i++ ) {
// Loop through the indices to keep and use these indices to
// select values from the inner array.
for ( j = 0, col_keep_count = columns_keep_num.length; j < col_keep_count; j++ ) {
// Capture the value to keep
var keep_val = range_values[i][columns_keep_num[j]];
// Write the value to the output using the offset method of the output range argument.
range_output.offset(i,j).setValue(keep_val);
}
}
}
function clearEmptyRows() {
var ss = SpreadsheetApp.openById('1GQiLt9utSH_6CV__oJwmcLOkI4E9iNIRPWU7Xr');
var s = ss.getActiveSheet();
var values = s.getDataRange().getValues();
nextLine: for( var i = values.length-1; i >=0; i-- ) {
for( var j = 0; j < values[i].length; j++ )
if( values[i][j] != "" )
continue nextLine;
s.deleteRow(i+1);
}
//I iterate it backwards on purpose, so I do not have to calculate the indexes after a removal
}
function clearSourceData() {
var ss = SpreadsheetApp.openById('1a89ZIUcy-8168D1damCV3Q9Ix0arQn9jGS6pgp');
var s = ss.getActiveSheet();
var data = s.getDataRange().getValues();
for(var n =data.length+1 ; n<0 ; n--){
if(data[n][0]!=''){n++;break}
}
s.deleteRows(2, (s.getLastRow()-1));
}
This is how it works :
function removeEmptyRows(){
var sh = SpreadsheetApp.getActiveSheet();
var maxRows = sh.getMaxRows();
var lastRow = sh.getLastRow();
sh.deleteRows(lastRow+1, maxRows-lastRow);
}
Note : you can handle columns the same way if necessary using getMaxColumn(), getLastColumn() and deleteColumns(number, howMany)
EDIT
by the way, here is also another way to delete empty rows in a spreadsheet... if you combine both it will "clean" your sheet entirely !
function deleteEmptyRows(){
var sh = SpreadsheetApp.getActiveSheet();
var data = sh.getDataRange().getValues();
var targetData = new Array();
for(n=0;n<data.length;++n){
if(data[n].join().replace(/,/g,'')!=''){ targetData.push(data[n])};
Logger.log(data[n].join().replace(/,/g,''))
}
sh.getDataRange().clear();
sh.getRange(1,1,targetData.length,targetData[0].length).setValues(targetData);
}
Demo sheet in view only - make a copy to use
Script to removeEmptyRows and removeEmptyColumns in Google Sheets. It puts together everything Serge and apptailor mentioned previously. Here is a sample sheet with the script included File > Make a copy... to edit a copy of the sheet. Also a video that shows you how to use this sheet.
//Remove All Empty Columns in the Entire Workbook
function removeEmptyColumns() {
var ss = SpreadsheetApp.getActive();
var allsheets = ss.getSheets();
for (var s in allsheets){
var sheet=allsheets[s]
var maxColumns = sheet.getMaxColumns();
var lastColumn = sheet.getLastColumn();
if (maxColumns-lastColumn != 0){
sheet.deleteColumns(lastColumn+1, maxColumns-lastColumn);
}
}
}
//Remove All Empty Rows in the Entire Workbook
function removeEmptyRows() {
var ss = SpreadsheetApp.getActive();
var allsheets = ss.getSheets();
for (var s in allsheets){
var sheet=allsheets[s]
var maxRows = sheet.getMaxRows();
var lastRow = sheet.getLastRow();
if (maxRows-lastRow != 0){
sheet.deleteRows(lastRow+1, maxRows-lastRow);
}
}
}
Just a quick note, I added this "if" statement to keep Serge insas's code from throwing an error if there is no empty bottom row when you are trying to remove empty rows.
Place this if around the last line function removeEmptyRows() and it
will not throw an error:
if (maxRows-lastRow != 0){
sh.deleteRows(lastRow+1, maxRows-lastRow);
}
Removing all empty lines (bottom-up)
before
after
function isEmptyRow(row){
for (var columnIndex = 0; columnIndex < row.length; columnIndex++){
var cell = row[columnIndex];
if (cell){
return false;
}
}
return true;
}
function removeEmptyLines(sheet){
var lastRowIndex = sheet.getLastRow();
var lastColumnIndex = sheet.getLastColumn();
var maxRowIndex = sheet.getMaxRows();
var range = sheet.getRange(1, 1, lastRowIndex, lastColumnIndex);
var data = range.getValues();
sheet.deleteRows(lastRowIndex+1, maxRowIndex-lastRowIndex);
for (var rowIndex = data.length - 1; rowIndex >= 0; rowIndex--){
var row = data[rowIndex];
if (isEmptyRow(row)){
sheet.deleteRow(rowIndex + 1);
}
}
}
function removeEmptyLinesFromAllSheets(){
SpreadsheetApp.getActive().getSheets().forEach(removeEmptyLines);
}
Removing only empty lines from below and above the data
before
after
function isEmptyRow(row){
for (var columnIndex = 0; columnIndex < row.length; columnIndex++){
var cell = row[columnIndex];
if (cell){
return false;
}
}
return true;
}
function getFirstNonBlankRowIndex(data){
for (var rowIndex = 0; rowIndex < data.length; rowIndex++){
var row = data[rowIndex];
if (!isEmptyRow(row)){
return rowIndex;
}
}
return 0;
}
function removePaddedEmptyLines(sheet){
var lastRowIndex = sheet.getLastRow();
var lastColumnIndex = sheet.getLastColumn();
var maxRowIndex = sheet.getMaxRows();
var range = sheet.getRange(1, 1, lastRowIndex, lastColumnIndex);
var data = range.getValues();
var firstRowIndex = getFirstNonBlankRowIndex(data);
sheet.deleteRows(lastRowIndex+1, maxRowIndex-lastRowIndex);
sheet.deleteRows(1, firstRowIndex);
}
function removePaddedEmptyLinesFromAllSheets(){
SpreadsheetApp.getActive().getSheets().forEach(removePaddedEmptyLines);
}
I have tried this piece of code and it works good, you may take a look and try it:
function DeleteBlankRows(){
var sh = SpreadsheetApp.getActiveSheet();
var maxRows = sh.getMaxRows();
var lastRow = sh.getLastRow();
for (var Raw = 1; Raw < sh.getLastRow() ; Raw++)
{
if( sh.getRange('A'+Raw).getValue() == '')
{
sh.deleteRow(Raw) //deleteRows(lastRow+1, maxRows-lastRow);
}
}
This works perfectly for me.
function removeEmptyRows(){
var spreadsheet = SpreadsheetApp.openById("IDOFYOURSPREADSHEETFOUNDINURL");
var sh = SpreadsheetApp.setActiveSheet(spreadsheet.getSheets()[0]);
var maxRows = sh.getMaxRows();
var lastRow = sh.getLastRow();
sh.deleteRows(lastRow+1, maxRows-lastRow);
}
This version allows you to specify top rows you don't want removed and also to ignore columns after ignoreAfterCol in case you don't want some columns considered when you are looking for blanks:
function removeEmptyLines(sheet,ignoreFirstRows,ignoreAfterCol){
sheet=ss.getSheetByName('Sheet12')
//get data and boundaries
var allData = sheet.getRange(1,1,sheet.getMaxRows(),ignoreAfterCol).getValues();
var sheetLength = allData.length;
while(allData[allData.length-1].toString().replace(/,/g,'')=='') allData.pop();
var lastPopulatedRow = allData.length;
//delete empty rows from end
var rowsToDeleteFromEnd = sheetLength - lastPopulatedRow;
if(rowsToDeleteFromEnd > 0) sheet.deleteRows(lastPopulatedRow+1,rowsToDeleteFromEnd);
//iterate through rows and delete blanks one by one
for(var i=lastPopulatedRow-1; i>ignoreFirstRows; i--){
if(allData[i].toString().replace(/,/g,'')=='') sheet.deleteRow(i+1);
}
}
this will help to delete exactly what you want:
Plus point:
you can check as many columns as you want to identify if a row is empty
this will also delete blank rows that contain formula
improve performance: this script deletes directly the empty rows according to their position without iteration through all the rows.
function deleteBlankRows(start_row=4) {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
//temporarily insert last column to avoid affecting existing data
sheet.insertColumnsAfter(sheet.getMaxColumns(),1);
var lastRow = findLastRow();
var lastCol = sheet.getMaxColumns()
var temp_col = sheet.getRange(start_row,lastCol,lastRow-start_row,1)
//insert formula to show row position if any row is blank from column A to N (can adjust if needed)
sheet.getRange(start_row,lastCol).setFormula('=if(countif(A'+start_row+':N'+start_row+',"*?")=0,row(),0)').copyTo(temp_col)
//get a reversed list of rows position excluded non-empty rows
var rowsPosition = temp_col.getValues().filter(x => x != 0).reverse()
//delete empty rows from bottom to top
rowsPosition.forEach(function(rowPosition){
if (Number(rowPosition) > start_row) {
sheet.deleteRow(Number(rowPosition))
}
})
//finally, delete the temporary column
sheet.deleteColumn(lastCol)
}
function findLastRow() {
const sh = SpreadsheetApp.getActive().getActiveSheet();
const data = sh.getRange("A:L").getValues();
const mR = sh.getMaxRows();
const indexes = [];
data[0].forEach((_, ci) => {
let col = data.map(d => d[ci]);
let first_index = col.reverse().findIndex(r => r != '');
if (first_index != -1) {
let max_row = mR - first_index;
indexes.push(max_row);
}
});
last_row = indexes.length > 0 ? Math.max(...indexes) : 0;
return last_row;
}
function deleteblankRw(){
var sheet=SpreadsheetApp.getActive().getSheetByName('test')
var e=sheet.getRange('A'+sheet.getMaxRows()).getNextDataCell(SpreadsheetApp.Direction.UP).getRow()
for (k=2;k<=e;k++) {
if(sheet.getRange('A'+k).getValue()=='') {
sheet.deleteRow(k);
k=2;e--
if(k==e){break};
SpreadsheetApp.flush();
}
}
}
Looking to extend my javascript object, I want to find the minium and maximum of a multicolumn csvfile. I have looked up solutions but I cannot really grasp the right way. I found a solution here: Min and max in multidimensional array but I do not get an output.
My code that I have for now is here:
function import(filename)
{
var f = new File(filename);
var csv = [];
var x = 0;
if (f.open) {
var str = f.readline(); //Skips first line.
while (f.position < f.eof) {
var str = f.readline();
csv.push(str);
}
f.close();
} else {
error("couldn't find the file ("+ filename +")\n");
}
for (var i=(csv.length-1); i>=0; i--) {
var str = csv.join("\n");
var a = csv[i].split(","); // convert strings to array (elements are delimited by a coma)
var date = Date.parse(a[0]);
var newdate = parseFloat(date);
var open = parseFloat(a[1]);
var high = parseFloat(a[2]);
var low = parseFloat(a[3]);
var close = parseFloat(a[4]);
var volume = parseFloat(a[5]);
var volume1000 = volume /= 1000;
var adjusted_close = parseFloat(a[6]);
outlet(0, x++, newdate,open,high,low,close,volume1000,adjusted_close); // store in the coll
}
}
Edit
What if, instead of an array of arrays, you use an array of objects? This assumes you're using underscore.
var outlet=[];
var outletkeys=['newdate','open','high','low','close','volume','volume1000','adjusted_close'];
for (var i=(csv.length-1);i>0; i--) {
var a = csv[i].split(",");
var date = Date.parse(a[0]);
var volume = parseFloat(a[5],10);
outlet.push( _.object(outletkeys, [parseFloat(date,10) , parseFloat(a[1],10) , parseFloat(a[2],10) , parseFloat(a[3],10) , parseFloat(a[4],10) , parseFloat(a[5],10) , volume /= 1000 , parseFloat(a[6],10) ]) );
}
Then the array of the column 'open' would be
_.pluck(outlet,'open');
And the minimum it
_.min(_.pluck(outlet,'open'));
Edit2
Let's forget about underscore for now. I believe you need to get the maximum value on the second column, which is what you put in your open variable.
¿Would it help if you could have that value right after the for loop? For example
var maxopen=0;
for (var i=(csv.length-1); i>=0; i--) {
var a = csv[i].split(",");
var date = Date.parse(a[0]);
var newdate = parseFloat(date);
var open = parseFloat(a[1]);
maxopen=(open>maxopen)? open : maxopen; // open overwrites the max if it greater
...
...
outlet(0, x++, newdate,open,high,low,close,volume1000,adjusted_close);
}
console.log('Maximum of open is',maxopen);