I found myself having to process a string like:
foo=bar&foo1=foo%3Dbar%26foo2%3Dfoo%253Dbar
Into:
{
"foo": "bar",
"foo1": {
"foo": "bar",
"foo2": {
"foo": "bar"
}
}
}
My best attempt is:
function parse(input) {
try {
const parsed = JSON.parse(input);
return parseJSON(parsed);
} catch (err) {
const decodedInput = decodeURIComponent(input);
if (input.includes("&") && input.includes("=")) {
return input.split("&").reduce((json, part) => {
const [key, value] = part.split("=");
const decodedValue = decodeURIComponent(value);
return { ...json, [key]: parsePrimitive(decodedValue) };
}, {});
}
return decodedInput;
}
}
function parsePrimitive(input) {
if (!isNaN(input)) {
return Number(input);
}
if (input === "true" || input === "false") {
return input === "true";
}
return parse(input);
}
function parseJSON(input) {
return Object.entries(input).reduce((json, [key, value]) => {
let object = {};
if (typeof value === "object") {
if (Array.isArray(value)) {
object[key] = value;
} else {
object[key] = parseJSON(value);
}
} else {
const decodedValue = decodeURIComponent(value);
if (decodedValue.includes("&") && decodedValue.includes("=")) {
object[key] = parse(decodedValue);
} else {
object[key] = parsePrimitive(decodedValue);
}
}
return { ...json, ...object };
}, {});
}
If you try to run it, you're supposed to call parse(input)
However, it does fail for certain inputs
How can I make the perfect recursive algorithm for this kind of problem?
Thanks!
I reworked the algorithm using Object.fromEntries(new URLSearchParams()).
function parse(query) {
try {
return JSON.parse(query);
} catch {
if (!isNaN(query)) {
return Number(query);
}
if (typeof query !== "string") {
const obj = {};
for (const queryKey in query) {
if (query.hasOwnProperty(queryKey)) {
obj[queryKey] = parse(query[queryKey]);
}
}
return obj;
}
if (!query) {
return "";
}
if (query.toLowerCase().match(/^(true|false)$/)) {
return query.toLowerCase() === "true";
}
const object = Object.fromEntries(new URLSearchParams(query));
const values = Object.values(object);
if (values.length === 1 && values[0] === "") {
return query;
}
return parse(object);
}
}
const q = 'foo=bar&foo1=foo%3Dbar%26foo2%3Dfoo%253Dbar';
console.log(parse(q));
console.log('fetching larger example...');
fetch('https://gist.githubusercontent.com/avi12/cd1d6728445608d64475809a8ddccc9c/raw/030974baed3eaadb26d9378979b83b1d30a265a3/url-input-example.txt')
.then(response => response.text())
.then(parse)
.then(console.log);
.as-console-wrapper { max-height: 100% !important; top: 0; }