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?

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

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.

share|improve this answer
    
Thank you!! I'll study your answere and try to enhance what I've made before your post. – ZeZeN Jan 25 '16 at 9:14

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
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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