5
\$\begingroup\$

I am building a JS router and would like to have some help doing it. It will work like so:

A list of routes is given with a URL pattern attached in each one.

[
  { url : "/home"},
  { url : "/user/:id"}
]

I want a function that will find the appropriate pattern when we pass it a route URL and will return the object of parameters:

getRouteParams("/home");    // {params: {}}
getRouteParams("/user/42"); // {params: {id: "42"}}
getRouteParams("/contact"); // null

I tried to do something with some parts of code I have found on the web and here is what I ended up with:

var routes = [
  { url : "/home"},
  { url : "/user/:id"}
];

function getRouteParams(url) {
  var argsVal = null;
  var x = 0;
  var result = {};
  for(; routes.length; x++){
    var routeMatcher = new RegExp(routes[x].url.replace(/(:\w+)/g, '([\\w-]+)'));
    argsVal = url.match(routeMatcher);
    if(argsVal) {
      argsVal.shift();
      result.params = makeObj(argsVal, routes[x].url);
      break;
    }
  }
  return result;

  function makeObj(vals, url) {
    var routeParts = url.split('/');
    var options = {};
    for(var i=0, j=0; i<routeParts.length; i++) {
      if(routeParts[i].indexOf(":") !== -1) {
        options[routeParts[i].slice(1)] = vals[j++];
      }
    }
    return options;
  }
}

console.log(getRouteParams("/home"));    // {params: {}}
console.log(getRouteParams("/user/42")); // {params: {id: "42"}}
console.log(getRouteParams("/contact")); // null)

This is a bit more sophisticated but the idea is there. Could someone help me improve on what I did?

\$\endgroup\$
4
  • 1
    \$\begingroup\$ I edited my post, sorry for that, this is my first post... \$\endgroup\$
    – ZeZeN
    Jan 22, 2016 at 14:25
  • 1
    \$\begingroup\$ Looks like it should be on topic now, thanks! \$\endgroup\$
    – syb0rg
    Jan 22, 2016 at 14:28
  • 2
    \$\begingroup\$ Welcome to Code Review! I have rolled back the last edit. Please see what you may and may not do after receiving answers. \$\endgroup\$
    – Quill
    Jan 25, 2016 at 9:20
  • \$\begingroup\$ I posted a self response instead, hope this is what was expected... \$\endgroup\$
    – ZeZeN
    Jan 25, 2016 at 9:45

2 Answers 2

3
\$\begingroup\$

Your algorithm seems to be working.

Here is the few things I would do to improve it:

// The routes should be a parameter of the function
function getParams(routes, url) {
  var params = null;

  // Depending on the targeted browser, you could use the native forEach method
  routes.forEach(function (route) {
    // I personally prefer to use methods in here
    if (params === null && routeMatch(route.url, url)) {
      params = computeParams(route.url, url);
    }
  });

  return { params: params };

  function routeMatch(route, url) {
    // You don't need to use `new RegExp`
    // I've added ^ and $ to be sure that it would be working with nested routes
    var matcher = '^' + route.replace(/(:\w+)/g, '([\\w-]+)') + '$';
    return url.match(matcher);
  }

  function computeParams(route, url) {
    var routeParts = route.split('/');
    var urlParts = url.split('/');
    var options = {};

    for (var i = 0, nbOfParts = routeParts.length; i < nbOfParts; i++) {
      // I check that the url part exists
      // and use the binary operator ~ that simplifies the `indexOf` method
      if (urlParts[i] && ~routeParts[i].indexOf(':')) {
        options[routeParts[i].slice(1)] = urlParts[i];
      }
    }

    return options;
  }
}

var routes = [ { url: '/home' }, { url: '/user/:id' }, { url: '/user/:id/post/:postId' }, { url: '/user/:id/post/:postId/comment/:cId' } ];

printResult(getParams(routes, "/home").params, {});
printResult(getParams(routes, "/user/42").params, { id: '42' });
printResult(getParams(routes, "/user/42/post/52").params, { id: '42', postId: '52' });
printResult(getParams(routes, "/user/42/post/52/").params, null);
printResult(getParams(routes, "/user/42/post/52/comment/").params, null);
printResult(getParams(routes, "/user/42/post/52/comment/yolo-22").params, { id: '42', postId: '52', cId: 'yolo-22' });
printResult(getParams(routes, "/contact").params, {});

function printResult(result, expected) {
  console.log('expected: ', expected, 'result: ', result);
}

That's all I can think of.

\$\endgroup\$
1
  • \$\begingroup\$ Thank you!! I'll study your answere and try to enhance what I've made before your post. \$\endgroup\$
    – ZeZeN
    Jan 25, 2016 at 9:14
0
\$\begingroup\$

I improved my code in the meanwhile, before looking at what you suggested. I'll study how I could merge that with yours.

Here is my actual improved code :

var routes = [
  {url: "/users/:uid/pictures/:avatar"},
  {url: "/users/:uid"},
  {url: "/home"}
];


function getRouteParams(url) {

  var argsVal,
      argsNames,
      params = {};

  for(var x = 0; x < routes.length; x++){

    var currRoute = routes[x].url;
    var routeMatcher = new RegExp(currRoute.replace(/(:\w+)/g, '([\\w-]+)'));
    argsVal = url.match(routeMatcher);

    if(argsVal) {

      argsVal.shift();
      argsNames = currRoute.match(/(:\w+)/g);

      if(argsNames) {
        for(var y = 0; y < argsNames.length; y++){
          params[argsNames[y].slice(1)] = argsVal[y];
        }
      }

      return {
        params : params
      };

    }

  }
  return null;
}


console.log(getRouteParams("/users/1024/pictures/ok")); // {params:{uid:"1024", avatar: "ok"}
console.log(getRouteParams("/users/zezen")); // {params: {uid: "zezen"}}
console.log(getRouteParams("/home")); // {params: {}}
console.log(getRouteParams("/zezen")); // null
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.