0

EDIT: For an even more effective solution, see this post.

I have a Google Sheet (example here) with thousands of rows of data where dates are entered into Column A in order from top to bottom, with varying numbers of rows for each date, starting on Row 9. I have created a button to run a script (thanks to @Rene Olivo for the help given here) which hides all rows except for today and yesterday's dates.

What I'm trying to accomplish is to hide all entries except for the last two entered dates on the sheet, as opposed to today and yesterday's dates, but I am unsure how to specify that. This is my current script:

function hideRows() {
    var ss = SpreadsheetApp.getActiveSpreadsheet();
    var s = ss.getSheetByName("Example");
    var v = s.getRange("A:A").getValues();
    var today = new Date();
    var helper = new Date();
    var yesterday = new Date(helper.setDate(helper.getDate()-1));
    today.setHours(0, 0, 0, 0);
    yesterday.setHours(0, 0, 0, 0);
    for (var i = s.getLastRow(); i > 8; i--) {
        var t = v[i - 1];
        if (t != "") {
            var u = new Date(t);
            if (u < yesterday) {
                s.hideRows(i);
            }
        }
    }
}

What would the best way to handle this be? I'm also curious to know whether there is a more efficient way to do what this script is doing, as this one is taking a lot of time to complete.

J. Kubassek
  • 205
  • 2
  • 14

3 Answers3

2

I had to make some big changes to make it more efficient as it really was getting super slow but I'm glad to say it's working now, here is the working script, the comments explain each change made:

function hideRows() {
    var ss = SpreadsheetApp.getActiveSpreadsheet();
    var s = ss.getSheetByName("Example");
    var v = s.getRange("A9:A").getValues(); //Modified the range to start directly from where the data starts
    var lastDate = s.getRange("Example!A"+ s.getLastRow()).getValue(); //Getting the value of the latest row on column A
    var beforeLast = new Date(lastDate-1); // getting the second to last date based on the previous date
    beforeLast.setHours(0, 0, 0, 0);
        for(var i = s.getLastRow(); i > -1; i--) { //Changed 8 to -1 to account for the changed range
               if(new Date(v[i]).toString() == beforeLast.toString()){  //the values are being changed to strings to be able to compare them,
                                                                        //here is where the code determines the cell where it should stop hiding rows.
            var row = i //Variable where the number of the last cell to hide is kept
          }
           };
         s.hideRows(9,row) //Hiding rows starting from the 9th row as that's where the data begins
    }

This one should be able to recognize the last two days dynamically and take much less time to process the main difference is that this time all rows are hidden at once after the code determines which cell is the one where the hiding should stop.

Here is a code that would work even if the dates are not consecutive:

function hideRowsV2() {
    var ss = SpreadsheetApp.getActiveSpreadsheet();
    var s = ss.getSheetByName("Example");
    var v = s.getRange("A:A").getValues(); //Modified the range to start directly from where the data starts
    var lastDate = s.getRange("Example!A"+ s.getLastRow()).getValue(); //Getting the value of the latest row on column A
    var previous = lastDate
    var counter = 0
     for(var i = s.getLastRow(); i > -1; i--) {
      if ((v[i][0]).toString() !== previous.toString()){
        counter = counter + 1
        previous = (v[i][0])
        var row = i
        
        if (counter == 4){
          break
        }

      }
     }
     
s.hideRows(9,row-7) 
}
Rene Olivo
  • 526
  • 1
  • 10
  • Okay that is much faster, and we are actually getting the last two dates, but like you said, only if they are consecutive - I do in fact need to get the two most recent dates whether they are consecutive or not, as they will in most cases not be consecutive. – J. Kubassek Apr 28 '23 at 21:25
  • Were you able to figure anything else out with this Rene? – J. Kubassek May 02 '23 at 16:17
  • 1
    Hi! Sorry I have not been able to work on this as much as I wanted to but I do have this afternoon to give it another shot so please allow me a bit of extra time. I I can't make it work at the very least I will share my ideas to see if they help – Rene Olivo May 02 '23 at 16:29
  • Just in case you missed it, I updated my answer, please take a look and let me know if this solves the problem @J.Kubassek – Rene Olivo May 03 '23 at 12:34
  • Thank you for the updated version, it seemed to work well in my example sheet initially, but when I attempted to implement it on my actual sheet I kept running into this error message: `TypeError: Cannot read properties of undefined (reading '0')` I've updated my example sheet to match it but can't quite figure out why this would be happening - could it be due to having text in the first 8 header rows? – J. Kubassek May 03 '23 at 12:41
  • 1
    I took a look at the sheet you shared and it seems like the error message happens because for some reason the getLastRow() function thinks that there is data at the very last row on the sheet, it should only return the last row where there is data but it is at this moment returning the very last row, even after deleting the last row. Do you have an idea why this could be? – Rene Olivo May 03 '23 at 20:05
  • 1
    Aha! I had a few ARRAYFORMULAS in columns C, I, Z, AA and AB which were returning "" when IF conditions were not met. And sure enough, when I remove the "" instances, the script works! Thank you so much for all your help! – J. Kubassek May 03 '23 at 21:07
  • Bummer, I just ran into another issue - I need to keep two columns of checkboxes in P9:Q, but then this reintroduces the same error as before, since an empty checkbox is displayed as FALSE. Is there not a way to find the last row based on entries only in Column A? – J. Kubassek May 04 '23 at 20:05
  • I've re-asked this [here](https://stackoverflow.com/questions/76202362/only-use-data-from-column-a-when-determining-getlastrow) as it could be considered a new question. – J. Kubassek May 08 '23 at 16:09
1

UPDATE TO INITIATE FILTER FROM CODE:

Here is some sample code to trigger the inbuilt filter from code. It can be saved in macro.gs (and should appear in the macro list) or in code.gs and assigned to a control. This is the same as the manual filter I used before, but you can assign it to a button for the convenience of the user. I would be surprised if this weren't much faster than any code you can come up with, since the built-in filter was optimized by the makers of Google Sheets.

function dateFilter() {

  var spreadsheet = SpreadsheetApp.getActive();
  spreadsheet.getRange('A:B').activate();
  spreadsheet.getRange('A:B').createFilter();
  spreadsheet.getRange('A1').activate();

  var criteria = SpreadsheetApp.newFilterCriteria()
  .whenFormulaSatisfied('=A:A>max(A:A)-2')
  .build();
  
  spreadsheet.getActiveSheet().getFilter().setColumnFilterCriteria(1, criteria);

};

This is using the ranges for my sample data, so you will have to adjust them. Also, I just used applied it to the active sheet, but you can use whatever method to get the reference to the spreadsheet that needs to have the filter applied. You can use the name or gid to obtain it.


ORIGINAL:

Maybe you could just use a filter with a formula as the condition. Here is some pretend data:

enter image description here

And then I'll filter column A by condition:

enter image description here

using formula

enter image description here

enter image description here

This does allow for multiple entries on one date:

enter image description here

but you do have to retrigger the filter when data is added.

Chris Strickland
  • 3,388
  • 1
  • 16
  • 18
  • I appreciate this option, but find it is not quite as user-friendly an option as a simple button click in this application. – J. Kubassek May 01 '23 at 11:53
  • 1
    I didn't realize that this was user initiated. In that case, perhaps a macro to apply the built in filter, instead of rolling your own. awfk so I'll try to put something together when I come back – Chris Strickland May 01 '23 at 17:46
  • Take a look at that and see if that does what you need, and also I'd be very curious to know about the relative performance. – Chris Strickland May 04 '23 at 14:22
  • I'm not sure what I'm doing wrong, but even when I try to duplicate your example data, everything hides except the header row. – J. Kubassek May 04 '23 at 19:02
  • What is the latest date in your data? I see that when I set up the filter manually I used max(a:a)-2 as the condition, but in my code filter I used today()-2. If you don't have data on today or yesterday, then it will hide them all. max(a:a)-2 is probably the right one to use. (but instead of a:a use the column with your dates in it) – Chris Strickland May 05 '23 at 00:41
  • When the latest date in my data is May 5, 2023 and I use '=MAX(A:A)-2', a filter is added, but no rows are hidden, you can see the example of this on the sheet named Filter in my example if you like. – J. Kubassek May 08 '23 at 15:50
  • Yes. The formula should be =A:A>max(A:A)-2. – Chris Strickland May 09 '23 at 06:31
  • Hmm, that is still hiding all the rows for some reason. – J. Kubassek May 09 '23 at 15:45
0
function hideRows() {
    const ss = SpreadsheetApp.getActiveSpreadsheet();
    const sh = ss.getSheetByName("Example");
    const vs = sh.getRange("A9:A" + sh.getLastRow()).getValues()
    let arr = sh.getRange("A9:A" + sh.getLastRow()).getValues().flat().sort((a,b) => {return new Date(b).valufOf() - new Date(a).valueOf();}); 
    vs.forEach((e,i) => {
      let dtv = new Date(e).valueOf();
      let v0 = new Date(arr[0]).valueOf();
      let v1 = new Date(arr[1]).valueOf();
      if( dtv != v0 && dtv != v1) {
        sh.hideRows(i + 9);
      }
    })
}
Cooper
  • 59,616
  • 6
  • 23
  • 54
  • This is hiding all dates except for the most recent - I am attempting to keep the second most recent dates visible as well as the most recent. It is also taking a very long time to run on my thousands of rows. I continue to appreciate your efforts! – J. Kubassek May 01 '23 at 20:16