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?
2 Answers 2
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.
-
\$\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– Flambino2017年01月16日 01:41:12 +00:00Commented Jan 16, 2017 at 1:41
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;
}
Explore related questions
See similar questions with these tags.