Skip to content

Embed URL

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Dead simple straight-up performant interpolation
/**
* Outputs a new function with interpolated object property values.
* Use like so:
* var fn = makeInterpolator('some/url/{param1}/{param2}');
* fn({ param1: 123, param2: 456 }); // => 'some/url/123/456'
*/
var makeInterpolator = (function() {
var rc = {
'\n': '\\n', '\"': '\\\"',
'\u2028': '\\u2028', '\u2029': '\\u2029'
};
return function makeInterpolator(str) {
return new Function(
'o',
'return "' + (
str
.replace(/["\n\r\u2028\u2029]/g, function($0) {
return rc[$0];
})
.replace(/\{([\s\S]+?)\}/g, '" + o["$1"] + "')
) + '";'
);
};
}());
var url = makeInterpolator('http://something.com/{param}/{foo}?x={n}');
url({
param: '123',
foo: '456',
n: 789
});
@mathiasbynens

Blog post with explanation: http://james.padolsey.com/javascript/straight-up-interpolation/


Really clever code.

P.S. Nice use of URI templates syntax.
P.P.S. I use $0, $1, … as identifiers for arguments to String#replace callbacks, too! :)

@JamesMGreene

Why not do the first 3 replace calls with a single one instead? (Or am I missing something?)

@calvinmetcalf

leaflet has a more compact version that turns it into one regex

function leafletInterpolater(str, data) {
    return str.replace(/\{ *([\w_]+) *\}/g, function (str, key) {
        var value = data[key];
        if (value === undefined) {
            throw new Error('No value provided for variable ' + str);
        } else if (typeof value === 'function') {
            value = value(data);
        }
        return value;
    });
}

in this case you'd either do

leafletInterpolater('http://something.com/{param}/{foo}?x={n}',{
  param: '123',
  foo: '456',
  n: 789
})

or

var url = leafletInterpolater.bind(undefined,'http://something.com/{param}/{foo}?x={n}');
url({
  param: '123',
  foo: '456',
  n: 789
});
@qfox
.replace(/\{([\s\S]+?)\}/g, function($0, $1) {
        return '" + o["' + $1 + '"] + "';
      })

// =>

.replace(/\{([\s\S]+?)\}/g, '" + o["$1"] + "')

Unless I'm missing something? (In other words, you can use $1 inside the string for replace to do the same thing, young padolseywan.)

@qfox

Also, why .replace(/"/g, '\\$&') and not just .replace(/"/g, '\\"') ? Easier to read.

@padolsey
Owner

@qfox -- ah please excuse the verbose code -- tis remnant of legacy iterations. I shall correct it :)

@padolsey
Owner

@JamesMGreene: I tried something like this:

replace(/["\r\n]/g, '\\$&')

But since we would then be replacing a literal \n with \\\n it won't work. The compiled Function requires the actual literal string \\n but this cannot be generated in such a meta way without directly writing it AFAIK. (brain silently implodes)

Give it a try though -- I may have missed something very obvious...

@padolsey
Owner

@calvinmetcalf AFAICT that runs the replace(...) at runtime when passing the data, right? My intent with this interpolator was runtime performance, sacrificing creation-time performance, hence the creation of a compiled Function with a straight-up concatenation inside.

@padolsey
Owner

@mathiasbynens: tbh I wasn't inspired by RFC6570 -- but looking at it now it seems v. useful. I also found https://github.com/fxa/uritemplate-js

@mathiasbynens

Only special-casing \n and \r is not enough. Having \u2028 or \u2029 in the input string still throws an error. Just replace them with their (double-)escaped equivalents and you’ll be fine.

@padolsey
Owner

@mathiasbynens: cool, thanks! -- just edited it.

@calvinmetcalf

@padolsey I got what you were going for after I read your blog post

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.