Skip to content
UniCircle
MediumCodingExpress routing

Match paths against Express-style routes

Question NODE-COD-002

Write the core of a router like Express's. You are given route patterns in priority order, then request paths. For each path, print the first route that matches and the captured parameters, or 404.

Matching rules:

  • Drop everything from the first ? or # in the path (query string or fragment).
  • Split both the pattern and the path on /, ignoring empty pieces (so a trailing slash does not matter, and / has zero segments).
  • They match when they have the same number of segments and each pattern segment either equals the path segment exactly (case-sensitive) or is a parameter :name, which captures that path segment.

Constraints

  • 1 ≤ routes ≤ 100, 1 ≤ paths ≤ 1000
  • Segments contain letters, digits, - and _; parameter names are letters only and unique within a pattern.
  • Paths start with / and have no //.

Input and output

Input
Line 1: n Next n lines: route patterns Next line: m Next m lines: paths
Output
For each path: the matching pattern, a space, and the parameters as compact JSON in pattern order (`{}` if none); or `404`.

Examples

Input 1

3
/users
/users/:id
/users/:id/posts/:postId
4
/users/42
/users/42/posts/7?sort=new
/about
/users/

Output

/users/:id {"id":"42"}
/users/:id/posts/:postId {"id":"42","postId":"7"}
404
/users {}

Query strings and a trailing slash are ignored; /about matches nothing.

Input 2

2
/colleges/top
/colleges/:slug
2
/colleges/top
/colleges/iit-madras

Output

/colleges/top {}
/colleges/:slug {"slug":"iit-madras"}

The first matching route wins, so the literal route listed first takes /colleges/top.

Show hints
  • Compare segment lists; a segment starting with : matches anything non-empty.
  • Try routes in the given order and stop at the first match.
Show the explanation

Pre-split every pattern once. For each request path, strip the query and fragment, split it into segments, and test the routes in order:

JavaScript
function match(patternSegs, pathSegs) {
  if (patternSegs.length !== pathSegs.length) return null;
  const params = {};
  for (let i = 0; i < patternSegs.length; i++) {
    const p = patternSegs[i];
    if (p.startsWith(':')) params[p.slice(1)] = pathSegs[i];
    else if (p !== pathSegs[i]) return null;
  }
  return params;
}

Order matters, exactly as in Express: if /users/:id is registered before /users/me, a request for /users/me is captured as id = "me". Register specific literal routes first. Real routers compile patterns into regular expressions (the path-to-regexp library) and add features such as optional and wildcard segments and percent-decoding of parameters.

Original UniCircle question. Last reviewed 22 Oct 2026.