javascriptarraysgoogle-apps-script

GAS Filter() not returning expected results


In the following [data][1]:

|First Name|Last Name|Email | |--|---|----------| |A |a |A@email.com| |B |b |B@email.com| |C |c |C@email.com| |D |d |D@email.com| |E |e |E@email.com|

, I'm having an issue using filter() (ln 19 below) to extract only the emails of the users whose first and last name match the selected users from the last col and row of [eList][2]:

|Some other column|Name | |--|--| ||a,A, b,B| ||c,C| ||C,c, D,d, E,e|

Right now filter() is returning all emails. Can someone please explain what I'm doing wrong?

Here is the function:

//Extracts emails from data of selected users function
getEmailAddresses(e) {
  // Get the responses from the form.
  const responses = SpreadsheetApp.openById("SHEET_ID").getSheetByName("Form Responses 2");
  // Extract the selected names from the checkboxes.
  const eList = responses.getRange(responses.getLastRow(),2,1, 1).getValues();
  Logger.log("eList\n" + eList);

  // Get the spreadsheet and sheet
  const ss = SpreadsheetApp.openById("SHEET_ID");
  const shet = ss.getSheetByName('ECI');

  const data = shet.getRange(2, 1, shet.getLastRow() - 1, 3).getValues();

  Logger.log("Data\n" + data);

  // Filter the data based on selected names and extract email addresses.
  const emailAddresses = data.map((row) => row[2] || false).filter(Boolean);  // Filter() is not returning the emails of the selected users only, returns all emails
  Logger.log("Emails\n" + emailAddresses);

  // Return the email addresses as a comma-separated string.
  return emailAddresses.join(','); }

// Populates Name in the Form
onOpen(e) function popFormList() {
  var sheet = SpreadsheetApp.openById('SHEET_ID').getSheetByName("ECI");
  var form = FormApp.getActiveForm();

  // Populates values for eList
  var siteValues = sheet.getRange(2, 1, sheet.getLastRow() - 1, 2),
          values = siteValues.getValues();

  values.sort();
  Logger.log(values);

  /* Populates eList with Names from ECI sheet */
  var siteItem = form.getItems(FormApp.ItemType.CHECKBOX)
                   .filter(function (siteItem) {
    return siteItem.getTitle() === "Name";
  })[0].asCheckboxItem();
  siteItem.setChoiceValues(values);

} // Runs when form is submitted with selected users to extract the emails of users for comms
function main(e) {
  var formResponses = e.response,
      itemResponses = formResponses.getItemResponses(),
               body = "",
             emails = "";

  for (x = 0; x < itemResponses.length; x++) {
    if (itemResponses[x].getResponse() === "") {
      continue;
    }
    else {
      body += itemResponses[x].getItem().getTitle() + ": " + itemResponses[x].getResponse() + "\n";
    }   }   emails = getEmailAddresses(e);   subject = "ECI Request"   Logger.log(emails);   Logger.log(body);

  try {
    // GmailApp.sendEmail(emails, subject, body, { noReply: true});   }   catch (e) {
    Logger.log('Error: There was a problem please see below\n' + e);   } }

[1]: https://i.sstatic.net/fkcIID6t.png [2]: https://i.sstatic.net/GPfrcJoQ.png


Solution

  • Right now this line:

    const emailAddresses = data.map((row) => row[2] || false).filter(Boolean);
    

    is just filtering out empty values. You can see that eList doesn't appear in the expression at all.

    You can try changing it to this:

    const selectedNames = eList.split(', ').map(item => item.split(','));
    const emailAddresses = data.filter(row => {
      return selectedNames.some(fullName => {
        return fullName[0] === row[0] && fullName[1] === row[1] ||
               fullName[1] === row[0] && fullName[0] === row[1];
        });
    }).map((row) => row[2]);
    

    For that to work, you should also modify the following line:

    // Extract the selected names from the checkboxes.
    const eList = responses.getRange(responses.getLastRow(),2,1, 1).getValues();
    

    to this:

    const eList = responses.getRange(responses.getLastRow(),2,1, 1).getValue();
    

    What changed? The last s, so the method returns a string instead of a 2D array.

    EDIT - Previously, I had suggested the following code:

    const selectedNames = eList.split(', ').map(item => item.split(','));
    const emailAddresses = data.filter(row =>
                                       selectedNames.includes(row.slice(0, 2)) ||
                                       selectedNames.includes(row.slice(0, 2).reverse()))
                               .map((row) => row[2]);
    

    That doesn't work, of course: I fell victim to the good old passed by reference pitfall.