4

When I run the code below in debug mode I get the expected value on the first iteration of the for loop but null on the second as seen on the images:

First iteration: enter image description here

Second iteration: enter image description here

What am I dong wrong?

The code I am using is:

var newer_than = ' newer_than:2d'; //added for faster debugging
var subjectIdentifier = '"Ingress Portal Submitted: "';
var searchString = 'subject:'+subjectIdentifier+newer_than;

function getPortalName(string) {
  var myRegexp = /: (.+)/g;
  var match = myRegexp.exec(string);
  var portalName = match[1];
  return portalName;
}

function getPortalsSubmitted() {
  var threads = GmailApp.search(searchString);
  for (i=0; i<threads.length; i++) {
    var subject = threads[i].getFirstMessageSubject();

    var portalName = getPortalName(subject);
    var subDate = threads[i].getMessages()[0].getDate();

    Logger.log([portalName,subDate]);
  }
}

function updatePortals() {
  var threads = GmailApp.search('subject:"Ingress Portal"');
  for (i=0; i<threads.length; i++) {
    Logger.log(threads[i].getFirstMessageSubject());
  }
}
Luciano
  • 992
  • 1
  • 9
  • 17
  • I thought this would be relative to the `g` flag storing the `lastIndex` of the match. http://jsfiddle.net/XuQPp/1/ but seems like this wouldn't be an issue as you create a new regex object with each function call. – Fabrício Matté Jun 06 '13 at 12:30
  • 1
    Yes, it shouldn't have been an issue. Can you replace this pattern with more generic `/:\s*(.+)/g`? And still, I'd have used `split` here, by something like `return string.split(/:\s*/)[1];` probably. – raina77ow Jun 06 '13 at 12:33
  • Or even split on `/:/`, if the previous one is failing, then checking what's really in the second part of the string. ) – raina77ow Jun 06 '13 at 12:39
  • @raina77ow The more generic regex failed also. But I can confirm that the `split(/:\s*/)` method works. It still a mystery though why `exec()` is failing. – Luciano Jun 06 '13 at 12:56
  • 1
    Now that's weird. Indeed, that behavior is explainable if regexobject was the same all the time - but it's clearly not. Well, can you check this by removing `/g` modifier from the pattern? It's not required here anyway. – raina77ow Jun 06 '13 at 13:54
  • WOW.. removing the `g` modifier did solve the issue! What a mystery.. thanks @raina77ow – Luciano Jun 06 '13 at 14:11
  • 1
    Should I make it an answer? Actually, @FabrícioMatté already had one. ) – raina77ow Jun 06 '13 at 14:23
  • 1
    @raina77ow Feel free to answer if you can, I'll be a little busy in the next hours and I don't really like making up explanations. `:P` – Fabrício Matté Jun 06 '13 at 14:39
  • 2
    You're using a regex literal, so you're not creating the regex everytime. Literals don't have scope. And here is the [documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec?redirectlocale=en-US&redirectslug=JavaScript%2FReference%2FGlobal_Objects%2FRegExp%2Fexec#Finding_successive_matches) on the `g` flag behavior. – Henrique G. Abreu Jun 07 '13 at 14:54
  • Thanks @HenriqueAbreu this really explains a lot! So **with the `g` flag** the regex literal fails because it tries to match starting from the **previous string**'s `lastIndex` on the **new string**. **Removing the `g` flag works** because it matches over the whole string again! That could be an actual answer if you care to write! Thanks again! – Luciano Jun 07 '13 at 17:41
  • Before I can add an answer to your question, you'll need to provide the most important piece of information: which faction are you? ;) – sahmeepee Jul 24 '14 at 12:14

1 Answers1

13

Although this question was already answered on the comments, I'll make a proper answer.

One important issue to understand this problem is on the exec behavior when the regex have the g flag. Which, when called sequentially will try to look for the "next" match, even if you pass on a different string. Here is the documentation link on MDN.

And although MDN states that you should take care not to re-create the RegExp object (even a literal), because it might reset the lastIndex property. At least in Apps Script that's not true. If a regex literal is used in the exact same spot in the code over and over, Apps Script caches the regex and re-uses the same object.

This two effects combined meant you were triggering this "next match" behavior on your code unknowingly.

The easiest solution for you is to just drop the g flag, since you don't need it anyway (you're getting only the first result). But you could have also fixed this by replacing the var myRegexp = /: (.+)/g; line with var myRegexp = new RegExp(': (.+)','g');, forcing Apps Script to give you a new object.

I think a good lesson we can learn from this is: don't use a flag if you don't need it. Sometimes we're lazy and set flags without thinking, "just in case".

Henrique G. Abreu
  • 17,406
  • 3
  • 56
  • 65