1
\$\begingroup\$

I'm working on an isomorphic javascript module (query-hash) to handle query strings and base64 tokens. It's essentially a simple key-value object with methods for taking in data and giving it back in the format you ask for.

function queryToObject(queryString) {
    return queryString.split('&')
        .map(kv => kv.split('='))
        .reduce((p, kv) => {
            p[kv[0]] = decodeURIComponent(kv[1] || '').replace(/\+/g, ' ');

            return p;
        }, {});
}

For the "functional approach", mapping the array created by split() seems like unnecessary function calls. Should I be splitting the string in the reduce() method? Is there a better approach?

\$\endgroup\$
2
\$\begingroup\$

For this simple use case, you are correct that you could do all this work in a reduce step saving yourself an additional iteration over the array created by the split() operation. Now whether this is enough of a performance impact for your expected use cases to move away from the (probably clearer-to-understand) map-reduce pattern is something that perhaps you would need do some testing to decide how you want to approach the problem.

If for example, you ever foresee yourself adding a filter() step, then perhaps keeping separation between map - filter - reduce steps makes sense.

I do think there are some edge cases around your basic query-split approach which might not be handled well here. For example, what about parameters with array notation (i.e. param[]=foobar)? You can certialny do plenty of search on the internet to gets lots of ideas on how to approach the basic query string parsing logic.

\$\endgroup\$
1
  • \$\begingroup\$ Good review. It should be noted that the param[]-style array notation is an informal convention - but very common one! So handling it isn't a requirement for proper query string parsing, though it may be desirable. \$\endgroup\$
    – Flambino
    Jan 16 '17 at 1:41
2
\$\begingroup\$

Code seems fine to me, honestly. Keeping the map step separate is just as good as incorporating it into reduce, since nothing really requires it to be one way or the other.

I would be a bit more explicit in naming, though (e.g. params instead of just p).

Also, you can use array destructuring to clean up the code a little:

function queryToObject(queryString) {
  return queryString
    .split('&')
    .map(pair => pair.split('='))
    .reduce((params, [key, value]) => { // destructure key-value pair
        params[key] = decodeURIComponent(value || '').replace(/\+/g, ' ');
        return params;
    }, {});
}

Or, if you incorporate the 2nd split into the reduce:

function queryToObject(queryString) {
  return queryString
    .split('&')
    .reduce((params, pair) => {
        var [key, value] = pair.split('=');
        params[key] = decodeURIComponent(value || '').replace(/\+/g, ' ');
        return params;
    }, {});
}

A completely different approach might be:

function queryToObject(queryString) {
  var params = {};
  queryString.replace(/([^=&]+)=([^&]*)/g, function (_, key, value) {
    params[key] = decodeURIComponent(value || '').replace(/\+/g, ' ');
  });
  return params;
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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