Mike Rifgin
Mike Rifgin

Reputation: 10765

How to make my regex only return exact matches

I'm looking at building a super simple router in javaScript...so far I have this:

var match,routeMatcher,p;

var routes = {
    "/users/:uid/pictures/:uid" : "userpictures",
    "/users/:uid" : "user"
};

var url = "/users/1024/pictures/456";

for(var i in routes) {
    //need to cache this regex
    routeMatcher = new RegExp(i.replace(/:[^\s/]+/g, '([\\w-]+)'));
    if(url.match(routeMatcher)) {
        match = url.match(routeMatcher);
        p = routes[i];
        console.log(p);
    }
}

and here's a fiddle http://jsfiddle.net/D6txe/

This works. When I feed in a url like "users/1000" I get back the corresponding function name (just got to work out how to call the string as a function now). Problem is if I have two routes that are very similar as in my example the regex matches both. In the example above I'd ideally only like the second route to be matched but unfortunately both get matched, because of course the first is also a match.

Is there any way I can make the regex only return exact matches? I have another solution which would involve counting the forward slashes in the input url and only call routes with a matching number of forward slashes....but this seems rather inelegant.

Upvotes: 2

Views: 259

Answers (3)

Tomalak
Tomalak

Reputation: 338316

Here's a variant that caches your regular expressions (note the '^' and '$' in the regex):

function Router(routes) {
    var route, routeExp, matchers = {};

    for (route in routes) {
        if (routes.hasOwnProperty(route)) {
            routeExp = '^' + route.replace(/:[^\s/]+/g, '([\\w-]+)') + '$';
            matchers[route] = new RegExp(routeExp);
        }
    }

    return function(url) {
        var match;
        for (route in matchers) {
            if (matchers.hasOwnProperty(route)) {
                if (match = url.match(matchers[route])) {
                    return {
                        route: route,
                        name: routes[route],
                        match: match.slice(1)
                    };
                }
            }
        }
    }
}

usage:

var router = new Router({
    "/users/:uid/pictures/:uid" : "userpictures",
    "/users/:uid" : "user"
});

var result = router("/users/1024/pictures/456");

console.log(result);
/*
{
    route: "/users/:uid/pictures/:uid",
    name: "userpictures"
    match: ["1024", "456"]
}
*/

Upvotes: 2

nhahtdh
nhahtdh

Reputation: 56819

I am not totally sure whether this is what you need.

I use ^ and $ to make the regex matches the exact format of the path portion of the URL:

routeMatcher = new RegExp("^" + i.replace(/:[^\s\/]+/g, '([\\w-]+)') + "$");

If you are taking the path from window.location.pathname to do the matching, then it is probably OK.

Upvotes: 2

Amberlamps
Amberlamps

Reputation: 40478

Try this:

routeMatcher = new RegExp(i.replace(/:[^\s/]+/g, '([\\w-]+)') + "$");

If you also want to support a trailing / to also cover /users/1024/pictures/456/, you can change your RegExp as follows:

routeMatcher = new RegExp(i.replace(/:[^\s/]+/g, '([\\w-]+)') + "/?$");

Upvotes: 4

Related Questions