31

I'm using Mustache-style tags inside of AngularJS. What's the best regex to use to return an array of just the text inside the mustache braces?

Sample data:

"This could {{be }} a {{ string.with.dots_and_underscores }} of {{ mustache_style}} words which {{could}} be pulled."

Expected output:

['be','string.with.dots_and_underscores','mustache_style','could']
RandallB
  • 5,415
  • 6
  • 34
  • 47

5 Answers5

35

If you use a global search with .match, JavaScript won't give the capture groups in its array output. As such, you need to do it twice: Once to find the {{...}} pairs, then again to extract the names from within them:

str.match(/{{\s*[\w\.]+\s*}}/g)
   .map(function(x) { return x.match(/[\w\.]+/)[0]; });
Xophmeister
  • 8,884
  • 4
  • 44
  • 87
  • Was just about to suggest adding `[0]` to the match so that you get an array of strings instead of an array of arrays before the edit. Nice – Jeff Shaver Mar 19 '13 at 15:01
  • 1
    nit: this regex is a bit too lenient about the dot notation, allowing non-valid mustache variable names like `.variable` or `context..variable`. Here's a stricter regex: `{{\s*\w+(?:\.\w+)*\s*}}` – Stiggler Dec 28 '21 at 23:39
13

Mod of Douglas Crockford's String.prototype.supplant

This will interpolate any {param} you have between handleBars ({}). I know this answer is somewhat extensive, but I assumed the question was probably regarding interpolation -- either way, I'd advise the reader to study the regex, regardless.

clear();

function interpolate(str) {
    return function interpolate(o) {
        return str.replace(/{([^{}]*)}/g, function (a, b) {
            var r = o[b];
            return typeof r === 'string' || typeof r === 'number' ? r : a;
        });
    }
}

var terped = interpolate('The {speed} {color} fox jumped over the lazy {mammal}')({
    speed: 'quick',
    color: 'brown',
    mammal: 'dog'
});

console.log(terped);

Hope this helps

Cody
  • 9,785
  • 4
  • 61
  • 46
8

You could try doing this with exec() instead:

var list = [],
    x = '"This could {{be }} a {{ string }} of {{ mustache_style}} words which {{could}} be pulled."',
    re = /{{\s*([^}]+)\s*}}/g,
    item;

while (item = re.exec(x))
    list.push(item[1]);
woemler
  • 7,089
  • 7
  • 48
  • 67
6

Something like this

/{{\s?([^}]*)\s?}}/

The values would be in first group (you know, not the 0-group, the 1-group :))

One more point - this regex is captures everything between {{ and }}, so all the punctuation marks, braces, dots, etc. If you need only words (possibly separated by underscore or whitespace) this would be more useful for you:

/{{\s?[\w\s]*\s?}}/
J0HN
  • 26,063
  • 5
  • 54
  • 85
  • Actually, /g is kind of useless here, `*` is greedy by default – J0HN Mar 19 '13 at 14:51
  • In javascript land, I get this as the output: ["{{be }}", "be "]. With the /g, it was capturing everything as mustache-expressions... how do I get access to the 1-group? Answer: http://stackoverflow.com/questions/432493/how-do-you-access-the-matched-groups-in-a-javascript-regex – RandallB Mar 19 '13 at 14:52
  • OK I updated... so no whitespace, but dots and underscores. :) – RandallB Mar 19 '13 at 14:56
  • sorry, I missed the `^}` part when I commented – musefan Mar 19 '13 at 14:56
3

I really liked the answer @Cody provided, but ran into a scope issue if you wanted to make the object you pass more of a real object and not just a list, so I found an eval trick to change the scope so I thought I would share it.

function interpolate(str) {
    return function interpolate(o) {
        return str.replace(/{([^{}]*)}/g, function (a, b) {
            let r
            with(o){
              r = eval(b)
            }
            return r
        });
    }
}

var terped = interpolate('The {speed} {fox.color} {mammal[2]} jumped over the lazy {mammal[0]}')({
    speed: 'quick',
    fox: {
      color: 'brown'
    },
    mammal: ['dog', 'cat', 'fox']
});

console.log(terped)
Individual11
  • 374
  • 4
  • 16