2
\$\begingroup\$

I was looking for a similar JavaScript method for http_build_query but did not find but create my own and here I am a bit confused to decide the right approach.

Here is the problem. I have an object of params and need to create query string to append with URL with help of encodeURIComponent:

const params = { "name": ["alpha &", "beta"], "id": ["one", "&two=2"], "from": 1533735971000, "to": 1533822371147, "status": true }; 

Now there are two approaches when there is an array of strings are there. I name them pre-encode and post-encode for now.

pre-encode

Apply encodeURIComponent to individual items and then join all items.

const params = { "name": ["alpha &", "beta"], "id": ["one", "&two=2"], "from": 1533735971000, "to": 1533822371147, "status": true }; const output = Object.entries(params).map((pair) => { const [key, val] = pair; if (val instanceof Array && val.length > 0) { let pre_encode = val.map(encodeURIComponent).join(','); console.log({pre_encode}); const pre_qs = `${encodeURIComponent(key)}=${pre_encode}`; console.log({pre_qs}) return pre_qs; } else { return pair.map(encodeURIComponent).join('='); } }).join('&'); console.log({output});

Output

"name=alpha%20%26,beta&id=one,%26two%3D2&from=1533735971000&to=1533822371147&status=true"


post-encode

First, join all items then apply encodeURIComponent.

const params = { "name": ["alpha &", "beta"], "id": ["one", "&two=2"], "from": 1533735971000, "to": 1533822371147, "status": true }; const output = Object.entries(params).map((pair) => { const [key, val] = pair; if (val instanceof Array && val.length > 0) { let post_encode = encodeURIComponent(val.join(',')); console.log({post_encode}); const post_qs = `${encodeURIComponent(key)}=${post_encode}`; console.log({post_qs}) return post_qs; } else { return pair.map(encodeURIComponent).join('='); } }).join('&'); console.log({output});

Output

"name=alpha%20%26%2Cbeta&id=one%2C%26two%3D2&from=1533735971000&to=1533822371147&status=true"

Which approach is the more efficient and right and how to handle null and undefined values?

\$\endgroup\$

    1 Answer 1

    2
    \$\begingroup\$

    Your main question

    I have trouble understanding it, because:

    • minor reason:
      (unless I'm missing something obvious) whatever way you encode the array parts, the resulting query string is quite valid in both cases;
      so the only possible criterion for choosing a method rather than the other one would be to find which one is faster... and this sounds a bit overkill!
    • major reason:
      you initially referred to the PHP http_build_query() function, so I'd expect you work the same, i.e. for a given key: [val_1, val_2]:
      • you're currently returning key=val_1,val_2
      • while it should be key=val_1&key=val_2

    To match the latter case, I'd suggest something like this:

    const output = Object.entries(params).map( (pair) => { let [key, val] = pair; // normalize val to always an array val = (val instanceof Array) ? val : [val]; // encode key only once key = encodeURIComponent(key); // then make a different query string for each val member return val.map( (member) => `${key}=${encodeURIComponent(member)}` ).join('&'); } ).join('&'); 

    Your complementary question

    To implement null or undefined, first you have to decide how they must be managed: they might be simply omitted, or included with no value (its often useful to have query params where only its presence/absence is meaningful for the underlying application).

    Anyway you may simply add any of these capabilities; instead of

     return val.map( (member) => `${key}=${encodeURIComponent(member)}` ).join('&'); 

    you can include null/undefined params simply with:

    return val.map( (member) => (member ? `${key}=${encodeURIComponent(member)}` : `${key}`) ).join('&'); 

    (NOTE: it's up to you to enhance this with more precise tests if desired, in order to keep 0 as key=0, or '' as key=)

    or you can omit them with a slightly different method:

    return val.reduce( (result, member) => { if (member) { result.push(`${key}=${encodeURIComponent(member)}`); } return result; }, [] ).join('&'); 
    \$\endgroup\$
    4
    • \$\begingroup\$Thank you for the suggestions. I was using .filter to remove null and undefined key and value prior to encoding. additionally using I need query in key=val_1,val_2 format and I think that is right way instead of adding each key and value separately. isn't it. does server treat these both key=val1&key=val2 , key=val1,val2 as same or it depends on mechanism how they read the query params in my case BE is java?\$\endgroup\$
      – xkeshav
      CommentedAug 11, 2018 at 21:23
    • 1
      \$\begingroup\$@pro.mean I can't say anything really pertinent about java. But you referred to PHP, and you can check that it's how it works. Subsequently I tend to think that it'd be the same with java, since browsers are necessarily agnostic with regard to server languages. In PHP, a query string like key=val1&key=val2 is "rendered" inside of the $_GETarray as a member like 'key' => ['val1', 'val2'].\$\endgroup\$
      – cFreed
      CommentedAug 11, 2018 at 23:28
    • \$\begingroup\$if (member) does not prevent null value\$\endgroup\$
      – xkeshav
      CommentedAug 13, 2018 at 6:53
    • \$\begingroup\$@pro.mean I must insist: if (member) prevents null, undefined, 0, and '' (it's why I added a comment about the two latters). Tested!\$\endgroup\$
      – cFreed
      CommentedAug 13, 2018 at 11:54

    Start asking to get answers

    Find the answer to your question by asking.

    Ask question

    Explore related questions

    See similar questions with these tags.