>>> import re
>>> route = re.compile(r'(?P<fb>^/foo/bar$)|(?P<fbd>^/foo/bar/\d+$)')
>>> route.match('/foo/bar').groupdict()
{'fb': '/foo/bar', 'fbd': None}
>>> route.match('/foo/bar/1').groupdict()
{'fb': None, 'fbd': '/foo/bar/1'}
If the fb group is set, act on the first route. If the fbd group is set, act on the second.Nice to know that what I made is considered the best solution algorithmically.
> /(^\/foo\/bar$)|(^\/foo\/bar\/\d+$)/.exec('/foo/bar')
[ '/foo/bar',
'/foo/bar',
undefined,
index: 0,
input: '/foo/bar' ]
> /(^\/foo\/bar$)|(^\/foo\/bar\/\d+$)/.exec('/foo/bar/3')
[ '/foo/bar/3',
undefined,
'/foo/bar/3',
index: 0,
input: '/foo/bar/3' ]
EDIT: Markdown differences.You could write a simple router based on this in < 50 lines of JS. I'd do it now, but I have work to do.
Read my top-level post again - multiple routes can be called on the same request, so express has to be able to find all of the matches, not just the first one. This is a mistake in the original article, as the author doesn't appear to understand the power of express routers.
> Second, you know which one matched based on the index of the capture groups, which is deterministic.
Deterministic, yes. But now you're also writing your own regex parser, because you have to know how many capture groups each sub-regex contains in order to figure out the indexing.
"/foo/bar/3".match(/((^\/foo\/bar\/.*)|(^\/foo\/bar\/(\d+)$)|(^\/baz))/) =>
Additionally, parsing out the number of capture groups in a regexp is simple, just looked for unescaped paren groups. You can do it once at route definition.Don't forget non-matching groups! Which brings up my point yet again - everyone is pretending this is a dead-simple problem to solve, but all of the proposed solutions are buggy. Not really an option for such a widely-used library.
I'm not claiming it's impossible. I'm claiming it's more difficult than anyone is willing to admit.
That being said, most frameworks don't have multiple matching routes, and I can't imagine that many people miss them. I've never used a framework that supported that, and I can't say I've ever felt I've missed out. Middleware is the right approach to that problem generally.
The point being, now you're describing a completely different, less powerful framework. Which, yes, of course it's faster. You'll notice that's what Netflix ended up doing - they moved to `restify` from `express`.
You might as well just only allow routes to be strings and then you have the constant-time map implementation that the article describes as the expected implementation.
Iterating through the array looking for the first non-null value, then looking it up in a JS object O(1) matching capture group positions to routes is much faster than having to dispatch a bunch of functions. Additionally, it can be JITed to much faster code I would wager.
So, expressJS = N regexp matches, while this method = 1 regexp match + N null checks in the resulting array + 1 hashmap lookup in an object to return the proper route. The second solution is going to be much faster, even for large routing tables, esp. given how optimizable that array lookup code is.
As far as multiple matches, after you found a match, you would then test against a new regex that is everything to the right of the previous regex's captured group.
((a)|(b)|(c)|(d)) -> if b matches, you then test against ((c)|(d)), and so on.
And how would you know which one got matched? The regex match isn't going to tell you that. Also, it needs to recognize if multiple were matched, which is definitely not going to be done by the built-in regex matcher.
It's certainly possible, but pretending it's trivial isn't helping, either.