martwetzels martwetzels - 1 month ago 7
Node.js Question

Regex in Node.js not showing all matches

To prepare a request-url for an API-call I am using RegEx to replace values from a String with values from an object.

Example of a 'template-string':

'https://api.fitbit.com/1/user/:ownerId/:collectionType/date/:date.json'


where :ownerId, :collectionType and :date will be replaced by the values from the following object:

{ collectionType: 'activities',
date: '2016-10-12',
ownerId: 'xxxxx',
ownerType: 'user',
subscriptionId: 'xxx' }


The regular expression I am using is:

/:([\w]+)/gi


This allows me to use the content of the group in every match to fetch the value from the object. (the match without the ':' in this case) I have the following function to return the request-url: (url is the 'template-string' and decoded is the object above)

function regexifyUrlTemplate(url, regex, decoded) {
console.log('Regexify URL: ', url)
console.log('Regexify Data: ', decoded)
var m
while (( m = regex.exec(url)) !== null) {
if (m.index === regex.lastIndex) {
regex.lastIndex++
}
console.log('Matches: ', m)
url = String(url).replace(m[0], decoded[m[1]])
console.log('Replaced ' + m[0] + ' with ' + decoded[m[1]])
}
console.log('Regexify :', url)
return url
}


The console shows me the following logs:

Regexify URL: https://api.fitbit.com/1/user/:ownerId/:collectionType/date/:date.json
Regexify Data: { collectionType: 'activities',
date: '2016-10-12',
ownerId: 'xxx',
ownerType: 'user',
subscriptionId: 'xxx' }

Matches: [ ':ownerId',
'ownerId',
index: 31,
input: '\'https://api.fitbit.com/1/user/:ownerId/:collectionType/date/:date.json\'' ]
Replaced :ownerId with xxx
Matches: [ ':date',
'date',
index: 59,
input: '\'https://api.fitbit.com/1/user/xxx/:collectionType/date/:date.json\'' ]
Replaced :date with 2016-10-12

Regexify : 'https://api.fitbit.com/1/user/xxx/:collectionType/date/2016-10-12.json'


It successfully replaces :ownerId and :date but does not replace :collectionType. I verified the RegEx at regex101.com and went through the regex with the updated strings from the logs.

Can anyone explain why :collectionType is not being matched? The only difference I can spot is the 'T' but with [\w] that should not matter. (also tried [a-zA-Z]).

Answer

The problem was here url = String(url).replace(m[0], decoded[m[1]])

You modify url during the exec(), so matche's index change ...

var url = 'https://api.fitbit.com/1/user/:ownerId/:collectionType/date/:date.json';

var data = { collectionType: 'activities',
   date: '2016-10-12',
   ownerId: 'xxxxx',
   ownerType: 'user',
   subscriptionId: 'xxx' }

var reg = /\:([\w]+)/gi;

function regexifyUrlTemplate(url, regex, decoded) {
  console.log('Regexify URL: ', url)
  console.log('Regexify Data: ', decoded)
  
  var m, originUrl = url;
  while (( m = regex.exec(originUrl)) !== null) {
    //if (m.index === regex.lastIndex) {
    //  regex.lastIndex++
    //}
    console.log('Matches: ', m)
    url = String(url).replace(m[0], decoded[m[1]])
    console.log('Replaced ' + m[0] + ' with ' + decoded[m[1]])
  }
  console.log('Regexify :', url)
  return url
}

regexifyUrlTemplate(url, reg, data)