{"id":1172,"date":"2021-06-15T14:30:02","date_gmt":"2021-06-15T14:30:02","guid":{"rendered":"https:\/\/www.ishygddt.xyz\/~blog\/?p=1172"},"modified":"2023-04-19T11:18:04","modified_gmt":"2023-04-19T16:18:04","slug":"javascript-parsing-query-parameters","status":"publish","type":"post","link":"http:\/\/www.ishygddt.xyz\/~blog\/2021\/06\/javascript-parsing-query-parameters","title":{"rendered":"Javascript: Parsing query parameters"},"content":{"rendered":"<p>There are <em>at least<\/em> 2\u00d75\u00d74\u00d73\u00d73\u00d71=360 different things you might unambiguously mean by \"parsing\" a query string in Javascript:<\/p>\n<ul>\n<li>What <strong>data type<\/strong> do you want returned? (Below code provides both these)\n<ul>\n<li><code class=\"language-javascript\" data-line=\"\">Map<\/code><\/li>\n<li><code class=\"language-javascript\" data-line=\"\">Object<\/code><\/li>\n<\/ul>\n<\/li>\n<li>How do you want <strong>duplicate keys<\/strong> handled? (Below code provides all these)\n<ul>\n<li>Just the first item (like built-in <code class=\"language-javascript\" data-line=\"\">URLSearchParams<\/code>)<\/li>\n<li>Just the last item (like PHP's <code class=\"language-php\" data-line=\"\">parse_str<\/code>; below code prefers this)<\/li>\n<li>All the items, as an <code class=\"language-javascript\" data-line=\"\">Array<\/code> (like Python's <code class=\"language-python\" data-line=\"\">parse_qs<\/code>)<\/li>\n<li>All the items, as a <code class=\"language-javascript\" data-line=\"\">Set<\/code><\/li>\n<li>Throw an error<\/li>\n<\/ul>\n<\/li>\n<li>How do you want instances of <code class=\"\" data-line=\"\">\u2026&amp;=val&amp;\u2026<\/code> (<strong>empty key name<\/strong>) handled?\n<ul>\n<li>Take the key to be the empty string (like built-in <code class=\"language-javascript\" data-line=\"\">URLSearchParams<\/code>; like Python's <code class=\"language-python\" data-line=\"\">parse_qs<\/code>; below code chooses this)<\/li>\n<li>Ignore them entirely (like PHP's <code class=\"language-php\" data-line=\"\">parse_str<\/code>)<\/li>\n<li>Throw an error<\/li>\n<\/ul>\n<\/li>\n<li>How do you want instances of <code class=\"\" data-line=\"\">\u2026&amp;key=&amp;\u2026<\/code> (<strong>empty value<\/strong>) handled?\n<ul>\n<li>Take the value to be the empty string (like built-in <code class=\"language-javascript\" data-line=\"\">URLSearchParams<\/code>; below code chooses this)<\/li>\n<li>Ignore them entirely (like Python's <code class=\"language-python\" data-line=\"\">parse_qs<\/code>)<\/li>\n<li>Reset the key; treat them as a <code class=\"language-javascript\" data-line=\"\">delete<\/code> directive<\/li>\n<li>Throw an error<\/li>\n<\/ul>\n<\/li>\n<li>How do you want instances of <code class=\"\" data-line=\"\">\u2026&amp;key&amp;\u2026<\/code> (<strong>missing an equals-sign<\/strong>) handled?\n<ul>\n<li>Take the key to be present but the value absent, by a constant such as <code class=\"language-javascript\" data-line=\"\">null<\/code> (below code chooses this)<\/li>\n<li><em>Pretend<\/em> there's an equals-sign; treat them identically to an empty value (like built-in <code class=\"language-javascript\" data-line=\"\">URLSearchParams<\/code>; like Python's <code class=\"language-python\" data-line=\"\">parse_qs<\/code>)<\/li>\n<li>Ignore them entirely, despite empty values being treated as the empty string<\/li>\n<li>Reset the key; treat them as a <code class=\"language-javascript\" data-line=\"\">delete<\/code>directive<\/li>\n<li>Throw an error<\/li>\n<\/ul>\n<\/li>\n<li>Do you want further <strong>parsing<\/strong> done on the values <em>within this function<\/em>?\n<ul>\n<li><a href=\"https:\/\/www.youtube.com\/results?search_query=type+juggling+exploit\">Haha no you don't<\/a><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>The below code demos some of the most important and useful combinations of these:<\/p>\n<pre><code class=\"language-javascript\" data-line=\"\">function _parse_qs_array(search) {\n\tif(search === undefined) search = window.location.search;\n\tlet m = search.match(\/^\\?(.+)$\/s);\n\tlet params = m ? m[1].split(&#039;&amp;&#039;) : new Array();\n\treturn params.map(s =&gt; {\n\t\tlet [key, value] = s.match(\/^(.*?)(?:=(.*))?$\/s).slice(1);\n\t\tkey = decodeURIComponent(key.replaceAll(&#039;+&#039;, &#039;%20&#039;));\n\t\tvalue = (value !== undefined ) ? decodeURIComponent(value.replaceAll(&#039;+&#039;, &#039;%20&#039;)) : null;\n\t\treturn [key, value];\n\t});\n}\n\n\nfunction parse_qs_map(search) {\n\t\/\/ Parses the given query string, returning the results as a Map.\n\t\/\/ Naked keys (without an equals-sign) are taken to have null values.\n\t\/\/ Duplicate keys take on the LAST value provided (like PHP&#039;s parse_str).\n\treturn new Map(_parse_qs_array(search));\n}\n\nfunction parse_qs_obj(search) {\n\t\/\/ Parses the given query string, returning the results as an Object.\n\t\/\/ Naked keys (without an equals-sign) are taken to have null values.\n\t\/\/ Duplicate keys take on the LAST value provided (like PHP&#039;s parse_str).\n\treturn Object.fromEntries(_parse_qs_array(search));\n}\n\n\nfunction parse_qs_map_first(search) {\n\t\/\/ Parses the given query string, returning the results as a Map.\n\t\/\/ Naked keys (without an equals-sign) are taken to have null values.\n\t\/\/ Duplicate keys take on the FIRST value provided (like URLSearchParams).\n\treturn new Map(_parse_qs_array(search).reverse());\n}\n\nfunction parse_qs_obj_first(search) {\n\t\/\/ Parses the given query string, returning the results as an Object.\n\t\/\/ Naked keys (without an equals-sign) are taken to have null values.\n\t\/\/ Duplicate keys take on the FIRST value provided (like URLSearchParams).\n\treturn Object.fromEntries(_parse_qs_array(search).reverse());\n}\n\n\nfunction parse_qs_map_all(search) {\n\t\/\/ Parses the given query string, returning the results as a Map with Array values.\n\t\/\/ Naked keys (without an equals-sign) are taken to have null values.\n\t\/\/ Duplicate keys are all returned, in-order.\n\tconst map = new Map();\n\t_parse_qs_array(search).forEach(([key, value]) =&gt; {\n\t\tif(! map.has(key) ) map.set(key, new Array());\n\t\tmap.get(key).push(value);\n\t});\n\treturn map;\n}\n\nfunction parse_qs_obj_all(search) {\n\t\/\/ Parses the given query string, returning the results as an Object with Array values.\n\t\/\/ Naked keys (without an equals-sign) are taken to have null values.\n\t\/\/ Duplicate keys are all returned, in-order.\n\tconst obj = new Object();\n\t_parse_qs_array(search).forEach(([key, value]) =&gt; {\n\t\tif( obj[key] === undefined ) obj[key] = new Array();\n\t\tobj[key].push(value);\n\t});\n\treturn obj;\n}\n\n\nfunction parse_qs_map_all_unique(search) {\n\t\/\/ Parses the given query string, returning the results as a Map with Set values.\n\t\/\/ Naked keys (without an equals-sign) are taken to have null values.\n\t\/\/ Duplicate keys are all returned. (Duplicate key-value pairs have no effect.)\n\tconst map = new Map();\n\t_parse_qs_array(search).forEach(([key, value]) =&gt; {\n\t\tif(! map.has(key) ) map.set(key, new Set());\n\t\tmap.get(key).add(value);\n\t});\n\treturn map;\n}\n\nfunction parse_qs_obj_all_unique(search) {\n\t\/\/ Parses the given query string, returning the results as an Object with Set values.\n\t\/\/ Naked keys (without an equals-sign) are taken to have null values.\n\t\/\/ Duplicate keys are all returned. (Duplicate key-value pairs have no effect.)\n\tconst obj = new Object();\n\t_parse_qs_array(search).forEach(([key, value]) =&gt; {\n\t\tif( obj[key] === undefined ) obj[key] = new Set();\n\t\tobj[key].add(value);\n\t});\n\treturn obj;\n}\n\n\nfunction parse_qs_map_1(search) {\n\t\/\/ Parses the given query string, returning the results as a Map.\n\t\/\/ Naked keys (without an equals-sign) are taken to have null values.\n\t\/\/ Duplicate keys will cause this function to throw a SyntaxError.\n\tconst map = new Map();\n\t_parse_qs_array(search).forEach(([key, value]) =&gt; {\n\t\tif( map.has(key) ) throw new SyntaxError(`${arguments.callee.name}: duplicate key`);\n\t\tmap.set(key, value);\n\t});\n\treturn map;\n}\n\nfunction parse_qs_obj_1(search) {\n\t\/\/ Parses the given query string, returning the results as an Object.\n\t\/\/ Naked keys (without an equals-sign) are taken to have null values.\n\t\/\/ Duplicate keys will cause this function to throw a SyntaxError.\n\tconst obj = new Object();\n\t_parse_qs_array(search).forEach(([key, value]) =&gt; {\n\t\tif( obj[key] !== undefined ) throw new SyntaxError(`${arguments.callee.name}: duplicate key`);\n\t\tobj[key] = value;\n\t});\n\treturn obj;\n}<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>There are <em>at least<\/em> 2\u00d75\u00d77\u00d73\u00d73\u00d71=630 different things you might unambiguously mean by \"parsing\" a query string in Javascript: \u2026<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[96,97],"tags":[40,72],"class_list":["post-1172","post","type-post","status-publish","format-standard","hentry","category-howto","category-original-content","tag-javascript","tag-parsing"],"_links":{"self":[{"href":"http:\/\/www.ishygddt.xyz\/~blog\/wp-json\/wp\/v2\/posts\/1172","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/www.ishygddt.xyz\/~blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.ishygddt.xyz\/~blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.ishygddt.xyz\/~blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"http:\/\/www.ishygddt.xyz\/~blog\/wp-json\/wp\/v2\/comments?post=1172"}],"version-history":[{"count":55,"href":"http:\/\/www.ishygddt.xyz\/~blog\/wp-json\/wp\/v2\/posts\/1172\/revisions"}],"predecessor-version":[{"id":1175,"href":"http:\/\/www.ishygddt.xyz\/~blog\/wp-json\/wp\/v2\/posts\/1172\/revisions\/1175"}],"wp:attachment":[{"href":"http:\/\/www.ishygddt.xyz\/~blog\/wp-json\/wp\/v2\/media?parent=1172"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.ishygddt.xyz\/~blog\/wp-json\/wp\/v2\/categories?post=1172"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.ishygddt.xyz\/~blog\/wp-json\/wp\/v2\/tags?post=1172"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}