Search code examples
javascriptgoogle-apps-scriptgoogle-sheetsgmail

Map function does not pass the last item in the list


I created a function that search's my inbox in Gmail and then classifies each massage in 3 categories (name, email and body). Then it pastes it in 3 columns in a Google Sheet. But, when I run it will only paste the name and email on the first 2 columns. The problems is with the body. Even though, when i run Logger.log(d.getPlainBody()); it shows the body text I am looking for.

The code I am using:

// extract emails from label in Gmail
function extractEmails() {
  
  // get the spreadsheet
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = ss.getActiveSheet();
  
  // get all email threads that match label from Sheet
  var threads = GmailApp.getInboxThreads();
  
  // get all the messages for the current batch of threads
  var messages = GmailApp.getMessagesForThreads(threads);
  
  var emailArray = [];
  
  // get array of email addresses
  messages.forEach(
    function(message) {
    message.forEach(
      function(d) {
      emailArray.push(d.getFrom(),d.getTo(),d.getPlainBody());
      Logger.log(d.getPlainBody());
    });
  });
  
  // de-duplicate the array
  var uniqueEmailArray = emailArray.filter(function(item, pos) {
    return emailArray.indexOf(item) == pos;
  });
  
  var cleanedEmailArray = uniqueEmailArray.map(
    function(el) {
      
      var matches = el.match(/\s*"?([^"]*)"?\s+<(.+)>/);
      
      if (matches) {
        name = matches[1]; 
        email = matches[2];
        body = matches[3];
      }
      
      return [name, email, body];
      
  });
  
  // clear any old data
  sheet.getRange(2,1,sheet.getLastRow(), 3).clearContent();
  
  // paste in new names and emails
  var printing = sheet.getRange(2 ,1,cleanedEmailArray.length, 3);
  printing.setValues(cleanedEmailArray);


}

Solution

  • Your logic seems faulty, especially on the map function. You are actually matching every element, not per message. And your regex is only capturing 2 patterns so it was expected for matches[3] to return blank.

    I modified your code a little bit. Here are they:

    1. Passing variables as an element of an array (message), and removed getTo as you are not using it. But if you are, feel free to add it again.
      // get array of email addresses
      messages.forEach(function(message) {
        message.forEach(function(d) {
          // Push the data as array to easily process them
          // Removed getTo as you only are getting name and email from getFrom data based on your regex
          emailArray.push([d.getFrom(),d.getPlainBody()]);
        });
      });
    
    1. Map function was replaced with a simpler forEach. This is possible since we made the message data as array above.
      var cleanedEmailArray = [];
      uniqueEmailArray.forEach(function(message){
        // match name and email from pattern "name <email>" in d.getFrom()
        // only pass match[1] and match[2], since match[0] = message[0]
        [, name, email] = message[0].match(/\s*"?([^"]*)"?\s+<(.+)>/);
        // get body from d.getPlainBody()
        body = message[1];
        cleanedEmailArray.push([name, email, body])
      });
    
    1. (Nitpick) You were not using printing so I combined the 2 lines
      // paste in new names and emails
      sheet.getRange(2 ,1,cleanedEmailArray.length, 3).setValues(cleanedEmailArray);
    

    This is what your code will look like:

    Code:

    function extractEmails() {
      var ss = SpreadsheetApp.getActiveSpreadsheet();
      var sheet = ss.getActiveSheet();
      var threads = GmailApp.getInboxThreads();
      var messages = GmailApp.getMessagesForThreads(threads);
      var emailArray = [], cleanedEmailArray = [];
    
      messages.forEach(function(message) {
        message.forEach(function(d) {
          emailArray.push([d.getFrom(),d.getPlainBody()]);
        });
      });
    
      var uniqueEmailArray = emailArray.filter(function(item, pos) {
        return emailArray.indexOf(item) == pos;
      });
      
      uniqueEmailArray.forEach(function(message){
        [, name, email] = message[0].match(/\s*"?([^"]*)"?\s+<(.+)>/);
        body = message[1];
        cleanedEmailArray.push([name, email, body])
      });
    
      sheet.getRange(2,1,sheet.getLastRow(), 3).clearContent();
      sheet.getRange(2 ,1,cleanedEmailArray.length, 3).setValues(cleanedEmailArray);
    }
    

    Output:

    output