google-sheetsgoogle-apps-script

How to ignore hidden rows?


I have a script that updates calendar events. At this time it takes 4 minutes to run. The majority of rows are past events and do not need updating. I need my script to only update rows that are not hidden. I've been reading a bunch of posts about this and the documentation, I've looked at the row hidden by user stuff and I just don't understand how to implement any of it. Can someone look at my script and tell me how to change it to ignore hidden rows? Here is my sheet.

Here is my script:

function updateEvents() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Working");
  const data = sheet.getDataRange().getValues();
  // Rows start at 2
  Logger.log(sheet.isRowHiddenByUser(2));

  if (data.length < 2) {
    console.warn("No data to process.");
    return;
  }

  const [headers, ...rows] = data;
  const eventIdIndex = headers.indexOf("onCalendar");
  const descriptionIndex = headers.indexOf("description");
  const locationIndex = headers.indexOf("location");

  //NEW STUFF - index of our file
  const docUrlIndex = headers.indexOf("docURL");

  if (eventIdIndex === -1 || descriptionIndex === -1) {
    console.error("Required columns 'onCalendar' or 'Description' are missing.");
    return;
  }

  const calendarIds = [
    "bus.transportation@robinson.k12.tx.us",
    "c_c17913bb97e7da2d5ea98cb36acd5d216ecae11f6bf8bd044d6d3e85009f8dca@group.calendar.google.com"
  ];

  calendarIds.forEach(calendarId => {
    const calendar = CalendarApp.getCalendarById(calendarId);

    rows.forEach((row, index) => {
      const eventId = row[eventIdIndex];
      if (!eventId) return;

      try {
        const event = calendar.getEventById(eventId);
        if (!event) {
          console.warn(`onCalendar ${eventId} not found (Row ${index + 2})`);
          return;
        }

        event.setDescription(row[descriptionIndex] || "");
        if (locationIndex !== -1) {
          event.setLocation(row[locationIndex] || "");
        }

        //NEW STUFF
        if (docUrlIndex !== -1 && row[docUrlIndex] != "") {
          //Calendar API event ID is the same as CalendarApp's but it doesnt end with @google.com
          const calendarApiEventId = eventId.replace("@google.com", "");

          //To avoid creating the whole resource manually, we get our existing event and then edit it later
          const resource = Calendar.Events.get(
            calendarId,
            calendarApiEventId
          );

          //Adding attachments
          resource["attachments"] = [{
            fileUrl: row[docUrlIndex],
            title: "Original Trip Sheet"
          }];

          //Updating our event
          Calendar.Events.update(
            resource,
            calendarId,
            calendarApiEventId, {
              supportsAttachments: true
            }
          )
        }

        console.info(`Updated event ID ${eventId} in calendar ${calendarId} (Row ${index + 2})`);

      } catch (error) {
        console.error(`Failed to update event ID ${eventId} in calendar ${calendarId} (Row ${index + 2}): ${error.message}`);
        console.error(`Error details: ${error.stack}`);
      }
    });
  });
}

Solution

  • Skip hidden rows

    Following the statement from a now deleted answer to this post. You can prevent hidden rows from being included on things that your program ultimately does, by adding this bit of code:

    onst rowIndex = index + 2; // Adding 2 because data starts from row 2 (index 1)
          
          // Skip this row if it's hidden
          if (sheet.isRowHiddenByUser(rowIndex)) {
            console.log(`Skipping hidden row ${rowIndex}`);
            return;
          }
    

    This modification uses a conditional statement that checks if a row is hidden and if so, the programs Execute the return statement effectively ignoring that hidden row.

    Complete code

    function updateEvents() {
      const ss = SpreadsheetApp.getActiveSpreadsheet();
      const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Working");
      const data = sheet.getDataRange().getValues();
      // Rows start at 2
      Logger.log(sheet.isRowHiddenByUser(2));
    
      if (data.length < 2) {
        console.warn("No data to process.");
        return;
      }
    
      const [headers, ...rows] = data;
      const eventIdIndex = headers.indexOf("onCalendar");
      const descriptionIndex = headers.indexOf("description");
      const locationIndex = headers.indexOf("location");
    
      //NEW STUFF - index of our file
      const docUrlIndex = headers.indexOf("docURL");
    
      if (eventIdIndex === -1 || descriptionIndex === -1) {
        console.error("Required columns 'onCalendar' or 'Description' are missing.");
        return;
      }
    
      const calendarIds = [
        "<calendar_id_here>",
        "<calendar_id_here>"
      ];
    
      calendarIds.forEach(calendarId => {
        const calendar = CalendarApp.getCalendarById(calendarId);
    
        rows.forEach((row, index) => {
          const rowIndex = index + 2; // Adding 2 because data starts from row 2 (index 1)
          
          // Skip this row if it's hidden
          if (sheet.isRowHiddenByUser(rowIndex)) {
            console.log(`Skipping hidden row ${rowIndex}`);
            return;
          }
    
          const eventId = row[eventIdIndex];
          if (!eventId) return;
    
          try {
            const event = calendar.getEventById(eventId);
            if (!event) {
              console.warn(`onCalendar ${eventId} not found (Row ${index + 2})`);
              return;
            }
    
            event.setDescription(row[descriptionIndex] || "");
            if (locationIndex !== -1) {
              event.setLocation(row[locationIndex] || "");
            }
    
            //NEW STUFF
            if (docUrlIndex !== -1 && row[docUrlIndex] != "") {
              //Calendar API event ID is the same as CalendarApp's but it doesnt end with @google.com
              const calendarApiEventId = eventId.replace("@google.com", "");
    
              //To avoid creating the whole resource manually, we get our existing event and then edit it later
              const resource = Calendar.Events.get(
                calendarId,
                calendarApiEventId
              );
    
              //Adding attachments
              resource["attachments"] = [
                {
                  fileUrl: row[docUrlIndex],
                  title: "Original Trip Sheet"
                }
              ];
    
              //Updating our event
              Calendar.Events.update(
                resource,
                calendarId,
                calendarApiEventId,
                { supportsAttachments: true }
              )
            }
    
            console.info(`Updated event ID ${eventId} in calendar ${calendarId} (Row ${index + 2})`);
    
          } catch (error) {
            console.error(`Failed to update event ID ${eventId} in calendar ${calendarId} (Row ${index + 2}): ${error.message}`);
            console.error(`Error details: ${error.stack}`);
          }
        });
      });
    }
    

    The sample output below is not a completely accurate representation of how your code works but this demonstrates the program successfully ignoring a hidden row.

    Sample Output

    No hidden row

    noHiddenRow

    With hidden row

    hiddenRow

    References: Conditional Statement