Converting Tree Like File Directory to a JSON object - javascript

I'm trying to convert a file directory response into a JSON object.
Here's a copy of the response from the file directory function.
[ 'C:/Users/Freddy/System/storage/Objects/Users/1',
'C:/Users/Freddy/System/storage/Objects/Users/1/email',
'C:/Users/Freddy/System/storage/Objects/Users/1/email/FreddyMcGee#Gmail.com',
'C:/Users/Freddy/System/storage/Objects/Users/1/etc',
'C:/Users/Freddy/System/storage/Objects/Users/1/etc/etc',
'C:/Users/Freddy/System/storage/Objects/Users/1/password',
'C:/Users/Freddy/System/storage/Objects/Users/1/password/123123123213',
'C:/Users/Freddy/System/storage/Objects/Users/1/username',
'C:/Users/Freddy/System/storage/Objects/Users/1/username/Freddy1337' ]
And this is the ouput that i'm trying/aiming to achieve:
1 : {
email: "FreddyMcGee#Gmail.com",
etc: etc,
password: "12313123",
username: "Freddy1337"
}
Simply the shortest path in the directory is the start of JSON object. All previous 'folder directories' are clipped.
I've attempted myself to write a function that does so, however I had some trouble since the folder 'Users' appears twice. Also the function doesn't traverse the nodes properly, it just cuts it at set sections and glues them together. It's very horrible, i'm a bit ashamed.
function TreeToJson(directory, cutAfter){
for (var i = directory.length - 1; i >= 0; i--) {
directory[i] = directory[i].substr(directory[i].indexOf(cutAfter) + cutAfter.length, directory[i].length - 1);
directory[i] = directory[i].split("/");
directory[i].shift();
};
jsonA = {}; jsonB = {}; jsonC = {};
for (var i = 0; i < directory.length; i++) {
if(directory[i][2] != undefined){
jsonB[directory[i][2]] = directory[i][3]
}
};
jsonC[Number([directory[0][1]])] = jsonB;
jsonA[directory[0][0]] = jsonC;
return jsonA;
}
TreeToJson(files, 'Objects');
If someone can show me a better approach into converting a 'Tree View Model' into a 'JSON Object' i'd appreciate it. I'm curious on the approaches other developers would take, and also what the most simplest solution would be.

A very common operation is extracting the part of the string after the last slash, so I'd make a regular expression function for that. Identify the starting directory name from the first element in the array, and then use a simple for loop to iterate through the rest of the array, two-by-two, extracting the keys and values:
const input = [
'C:/Users/Freddy/System/storage/Objects/Users/1',
'C:/Users/Freddy/System/storage/Objects/Users/1/email',
'C:/Users/Freddy/System/storage/Objects/Users/1/email/FreddyMcGee#Gmail.com',
'C:/Users/Freddy/System/storage/Objects/Users/1/etc',
'C:/Users/Freddy/System/storage/Objects/Users/1/etc/etc',
'C:/Users/Freddy/System/storage/Objects/Users/1/password',
'C:/Users/Freddy/System/storage/Objects/Users/1/password/123123123213',
'C:/Users/Freddy/System/storage/Objects/Users/1/username',
'C:/Users/Freddy/System/storage/Objects/Users/1/username/Freddy1337'
];
const lastPart = str => str.match(/\/([^\/]+)$/)[1];
const [baseDirectory, ...keysVals] = input;
const dirName = lastPart(baseDirectory);
const dirObj = {};
for (let i = 0; i < keysVals.length; i += 2) {
const key = lastPart(keysVals[i]);
const val = lastPart(keysVals[i + 1]);
dirObj[key] = val;
}
const output = { [dirName]: dirObj };
console.log(output);

you can split by 'Users' and .reduce() the resulting array :
const data = ['C:/Users/Freddy/System/storage/Objects/Users/1',
'C:/Users/Freddy/System/storage/Objects/Users/1/email',
'C:/Users/Freddy/System/storage/Objects/Users/1/email/FreddyMcGee#Gmail.com',
'C:/Users/Freddy/System/storage/Objects/Users/1/etc',
'C:/Users/Freddy/System/storage/Objects/Users/1/etc/etc',
'C:/Users/Freddy/System/storage/Objects/Users/1/password',
'C:/Users/Freddy/System/storage/Objects/Users/1/password/123123123213',
'C:/Users/Freddy/System/storage/Objects/Users/1/username',
'C:/Users/Freddy/System/storage/Objects/Users/1/username/Freddy1337'
];
const objects = data
.map(e => {
return e.split('Users')[2];
})
.reduce((all, curr) => {
let elems = curr.split('/');
all[elems[1]] = all[elems[1]] || {};
if ([elems[2]] && elems[3]) {
Object.assign(all[elems[1]], {
[elems[2]]: elems[3]
})
}
// elems[1] is : 1
// elems[2] is the key ( username, password .. )
// elems[3] is the value ( Freddy1337 ... )
return all;
}, {})
console.log(objects)
EDIT : same code above wrapped in a function :
const tree = ['C:/Users/Freddy/System/storage/Objects/Users/1',
'C:/Users/Freddy/System/storage/Objects/Users/1/email',
'C:/Users/Freddy/System/storage/Objects/Users/1/email/FreddyMcGee#Gmail.com',
'C:/Users/Freddy/System/storage/Objects/Users/1/etc',
'C:/Users/Freddy/System/storage/Objects/Users/1/etc/etc',
'C:/Users/Freddy/System/storage/Objects/Users/1/password',
'C:/Users/Freddy/System/storage/Objects/Users/1/password/123123123213',
'C:/Users/Freddy/System/storage/Objects/Users/1/username',
'C:/Users/Freddy/System/storage/Objects/Users/1/username/Freddy1337'
];
function TreeToJson(data, cutAfter){
const objects = data
.map(e => {
return e.split(cutAfter)[1];
})
.reduce((all, curr) => {
let elems = curr.split('/');
all[elems[2]] = all[elems[2]] || {};
if([elems[3]] && elems[4]){
Object.assign(all[elems[2]], {
[elems[3]] : elems[4]
})
}
return all;
}, {})
return objects;
}
console.log(TreeToJson(tree, 'Objects'))

Related

Transform array into object with custom properties

I have this array
myarr = [
'=title1',
'longText0...',
'longtText1...',
'=title2',
'longTextA...',
'longtTextB...',
'longtTextC...'
];
symbol = indicates that is is a property, next to that is a list of items that belongs to that property
I want to transform that array into object
myObj = {
title1: [
'longText0...',
'longtText1...',
],
title2: [
'longTextA...',
'longtTextB...',
'longtTextC...'
]
}
I come up with this code so far:
const arrayToObject = (array) =>
array.reduce((obj, item) => {
if(item.startsWith('=')) {
const itemName = item.replace('=', '')
obj[itemName] = itemName;
} else {
//add the rest....
}
return obj
}, {})
console.log(arrayToObject(myarr))
My challenges so far is that I am not sure how to turn obj[itemName] so I can assign the items to it. Any ideas how to do that?
A reduce based approach which does not depend on outer scope references for keeping track of the currently to be built/aggregated property makes this information part of the reducer function's first parameter, the previousValue which serves as an accumulator/collector object.
Thus, as for the OP's task, this collector would feature two properties, the currentKey and the result, where the former holds the state of the currently processed property name and the latter being the programmatically built result.
// - reducer function which aggregates entries at time,
// either by creating a new property or by pushing a
// value into the currently processed property value.
// - keeps the state of the currently processed property
// by the accumulator`s/collector's `currentKey` property
// whereas the result programmatically gets build as
// the accumulator`s/collector's `result` property.
function aggregateEntry({ currentKey = null, result = {} }, item) {
const key = (item.startsWith('=') && item.slice(1));
if (
(key !== false) &&
(key !== currentKey)
) {
// keep track of the currently processed property name.
currentKey = key;
// create a new entry (key value pair).
result[currentKey] = [];
} else {
// push value into the currently processed property value.
result[currentKey].push(item);
}
return { currentKey, result };
}
console.log([
'=title1',
'longText0...',
'longtText1...',
'=title2',
'longTextA...',
'longtTextB...',
'longtTextC...',
].reduce(aggregateEntry, { result: {} }).result);
.as-console-wrapper { min-height: 100%!important; top: 0; }
I wouldn't do that with reduce but with a simple for loop, because you have to carry the itemname over multiple iterations
let o = {}, n = '';
for (let k of arr) {
if (k.startsWith('=')) {
n = k.substring(1);
o[n] = []
} else {
o[n].push(k);
}
}
You can of course also do it with reduce, but you have to put the declaration of itemname outside of the callback
let n = '';
let o = arr.reduce((a, c) => {
if (c.startsWith('=')) {
n = c.substring(1);
a[n] = [];
} else {
a[n].push(c);
}
return a;
}, {});
Please be aware, there is no error handling, ie the code assumes your array is well structured and the first element in the array must start with =
The following function will give you the desired results
function arrayToObject(arr)
{
let returnObj={};
for(let i =0; i <arr.length; i++)
{
if(arr[i].startsWith('='))
{
let itemName = arr[i].replace('=','');
returnObj[itemName]=[];
for(let j=i+1; j <arr.length;j++)
{
if(arr[j].startsWith('='))
{
break;
}
else
{
let value = arr[j];
returnObj[itemName].push(value) ;
}
}
}
}
return returnObj;
}
Here a version with reduce
const myarr = [
'=title1',
'longText0...',
'longtText1...',
'=title2',
'longTextA...',
'longtTextB...',
'longtTextC...'
];
const obj = myarr.reduce((res, el) => {
if(el.startsWith('=')){
const key = el.substring(1)
return {
data: {
...res.data,
[key]: [],
},
key
}
}
return {
...res,
data:{
...res.data,
[res.key]: [...res.data[res.key], el]
}
}
}, {
data: {},
key: ''
}).data
console.log(obj)
You don't need to keep the key somewhere separate for a reduce method:
const myarr = ['=title1', 'longText0...', 'longtText1...', '=title2', 'longTextA...', 'longtTextB...', 'longtTextC...'];
const res = Object.fromEntries(
myarr.reduce((acc, item) => {
if(item.startsWith('='))
acc.push([item.substring(1), []]);
else
acc[acc.length - 1]?.[1].push(item);
return acc;
}, [])
);
console.log(JSON.stringify( res ));

How to generate array key using array index in for on the associative array in javascript

Is it possible to generate array keys using the array index in the "for" to create associative arrays?
I want the values in the index array in the "for" to be used as keys in the associative array
The sample code that you want to get the value for making an associative array is in the middle of the *** sign :
if(data.status == 422){
let msg = data.responseJSON.errors;
let msgObject = Object.keys(msg);
for (let i = 0; i < msgObject.length; i++) {
if (msg[msgObject[i]]) {
let msgObjectIndex = msgObject[i];
let columnIndex = {
||
\/
***msgObject[i]*** : column[msgObject[i]]
/\
||
};
console.log(columnIndex);
}
}
}else {
alert('Please Reload to read Ajax');
console.log("ERROR : ", e);
}
},
then variable column is:
let column = {
'nama_paket' : $('.edit_error_nama_paket'),
'tipe_berat' : $('.edit_error_tipe_berat'),
'harga' : $('.edit_error_harga'),
'id_service' : $('.edit_error_id_service'),
};
I tried the code above to get the following error: Uncaught SyntaxError: Unexpected token '['
thanks
You can generate computed property names with [] syntax
Simple example:
let obj = {};
['a','b','c'].forEach((e,i) => {
let computed = {[e]:i};
Object.assign(obj, computed)
})
console.log(obj)
Here couple of ways you can do, see my comments.
one way is as suggested by #charlietlf.
After object creation, add the key/value
let msg = data.responseJSON.errors;
let msgObject = Object.keys(msg);
for (let i = 0; i < msgObject.length; i++) {
if (msg[msgObject[i]]) {
let msgObjectIndex = msgObject[i];
const newKey1 = `MyNewKey${2 + 3}`; // sample
let columnIndex = {
// One way is
[newKey1]: column[msgObject[i]],
};
// Alternatively, we can add the generated key and value to obj
const newKey2 = msgObject[i];
// or something like newKey2 = `MyKey${msgObject[i]}`
columnIndex[newKey2] = column[msgObject[i]];
console.log(columnIndex);
}
}

regex to find pairs in array

I would like to parse that string:
[[abc.d.2,mcnv.3.we],[abec.d.2,mcnv.4.we],[abhc.d.2,mcnv.5.we]]
In order to have a key value (JSON)
{
"abc.d.2": "mcnv.3.we",
"abec.d.2: "mcnv.4.we",
"abhc.d.2": "mcnv.5.we"
}
First I would like to check if string can be parse to make it key=>value.
How can I check the string if it contains pairs?
Thanks
You can try something like this:
Approach 1:
Idea:
Update the regex to have more specific characters. In your case, alphanumeric and period.
Get all matching elements from string.
All odd values are keys and even matches are values.
Loop over matches and create an object.
const str = "[[abc.d.2,mcnv.3.we],[abec.d.2,mcnv.4.we],[abhc.d.2,mcnv.5.we]]";
const matches = str.match(/[\w\.\d]+/gi);
const output = {};
for(var i = 0; i< matches.length; i+=2) {
output[matches[i]] = matches[i+1];
}
console.log(output)
Approach 2:
Idea:
Write a regex to capture each individual group: [...:...]
Then eliminate braces [ and ].
Split string using comma ,.
First part is your key. Second is your value.
const str = "[[abc.d.2,mcnv.3.we],[abec.d.2,mcnv.4.we],[abhc.d.2,mcnv.5.we]]";
const matches = str.match(/\[([\w\d\.,]*)\]/gi);
const output = matches.reduce((obj, match) => {
const parts = match.substring(1, match.length - 1).split(',');
obj[parts[0]] = parts[1];
return obj;
}, {})
console.log(output)
In above approach, you can also include Map. The iteration can be bit confusing initially, but you can try.
const str = "[[abc.d.2,mcnv.3.we],[abec.d.2,mcnv.4.we],[abhc.d.2,mcnv.5.we]]";
const matches = str.match(/\[([\w\d\.,]*)\]/gi);
const output = matches.reduce((obj, match) => {
const parts = match.substring(1, match.length - 1).split(',');
obj.set(...parts)
return obj;
}, new Map())
for (const [k, v] of output.entries()) {
console.log(`Key: ${k}, value: ${v}`)
}
Parse the array as JSON, iterate over the array, adding entries to the target object as you go, watch out for duplicate keys:
let dict_target = {}; // The target dictionary,
let src, arysrc, proceed = false;
try {
src = "[[abc.d.2,mcnv.3.we],[abec.d.2,mcnv.4.we],[abhc.d.2,mcnv.5.we]]"
.replace(/,/g, '","')
.replace(/\]","\[/g, '"],["')
.replace(/^\[\[/, '[["')
.replace(/\]\]$/, '"]]')
;
arysrc = JSON.parse(src);
proceed = true; // Could parse the data, can carry on with processing the data
} catch (e) {
console.log(`Source data unparseable, error '${e.message}'.`);
}
if (proceed) {
arysrc.forEach ( (a_item, n_idx) => {
if (dict_target.hasOwnProperty(a_item[0])) {
// add any tests and processing for duplicate keys/value pairs here
if (typeof dict_target[a_item[0]] === "string") {
dict_target[a_item[0]] = [ dict_target[a_item[0]] ];
}
dict_target[a_item[0]].push(a_item[1]);
}
else {
dict_target[a_item[0]] = a_item[1];
}
});
} // if -- proceed
My coding golf solution...
const parse = (str) => {
let obj = {};
str.replace(
/\[([^\[,]+),([^\],]+)\]/g,
(m, k, v) => obj[k] = v
);
return obj;
};
Advantages:
More Permissive of arbitrary chars
More Tolerant of missing values
Avoids disposable objects for GC
Disadvantages:
More Permissive of arbitrary chars!
This is not a proper parser...
Does not have context, just [key,val]
I actually wanted to post the following as my answer... but I think it'll get me in trouble :P
const parse=(str,obj={})=>
!str.replace(/\[([^\[,]+),([^\],]+)\]/g,(m,k,v)=>obj[k]=v)||obj;
Here's the code which validates the string first and outputs the result. Not at all optimal but does the task just fine.
var string = '[[abc.d.2,mcnv.3.we],[abec.d.2,mcnv.4.we],[abhc.d.2,mcnv.5.we]]';
var result = (/^\[(\[.*\..*\..*\,.*\..*\..*\]\,)*\[(.*\..*\..*\,.*\..*\..*)\]\]$/g).exec(string);
if (result) {
var r1 = result[1].replace(/\[|\]/g, '').split(',');
var r2 = result[2].split(',');
var output = {};
for (var i = 0; i < r1.length -1; i +=2) {
output[r1[i]] = r1[i+1];
}
output[r2[0]] = r2[1];
console.log(output);
} else {
console.log('invalid string');
}

convert serialized form to individual post items [duplicate]

This question already has answers here:
How can I get query string values in JavaScript?
(73 answers)
Closed 1 year ago.
I have a string like this:
abc=foo&def=%5Basf%5D&xyz=5
How can I convert it into a JavaScript object like this?
{
abc: 'foo',
def: '[asf]',
xyz: 5
}
In the year 2021... Please consider this obsolete.
Edit
This edit improves and explains the answer based on the comments.
var search = location.search.substring(1);
JSON.parse('{"' + decodeURI(search).replace(/"/g, '\\"').replace(/&/g, '","').replace(/=/g,'":"') + '"}')
Example
Parse abc=foo&def=%5Basf%5D&xyz=5 in five steps:
decodeURI: abc=foo&def=[asf]&xyz=5
Escape quotes: same, as there are no quotes
Replace &: abc=foo","def=[asf]","xyz=5
Replace =: abc":"foo","def":"[asf]","xyz":"5
Suround with curlies and quotes: {"abc":"foo","def":"[asf]","xyz":"5"}
which is legal JSON.
An improved solution allows for more characters in the search string. It uses a reviver function for URI decoding:
var search = location.search.substring(1);
JSON.parse('{"' + search.replace(/&/g, '","').replace(/=/g,'":"') + '"}', function(key, value) { return key===""?value:decodeURIComponent(value) })
Example
search = "abc=foo&def=%5Basf%5D&xyz=5&foo=b%3Dar";
gives
Object {abc: "foo", def: "[asf]", xyz: "5", foo: "b=ar"}
Original answer
A one-liner:
JSON.parse('{"' + decodeURI("abc=foo&def=%5Basf%5D&xyz=5".replace(/&/g, "\",\"").replace(/=/g,"\":\"")) + '"}')
2022 ES6/7/8 and on approach
Starting ES6 and on, Javascript offers several constructs in order to create a performant solution for this issue.
This includes using URLSearchParams and iterators
let params = new URLSearchParams('abc=foo&def=%5Basf%5D&xyz=5');
params.get("abc"); // "foo"
Should your use case requires you to actually convert it to object, you can implement the following function:
function paramsToObject(entries) {
const result = {}
for(const [key, value] of entries) { // each 'entry' is a [key, value] tupple
result[key] = value;
}
return result;
}
Basic Demo
const urlParams = new URLSearchParams('abc=foo&def=%5Basf%5D&xyz=5');
const entries = urlParams.entries(); //returns an iterator of decoded [key,value] tuples
const params = paramsToObject(entries); //{abc:"foo",def:"[asf]",xyz:"5"}
Using Object.fromEntries and spread
We can use Object.fromEntries, replacing paramsToObject with Object.fromEntries(entries).
The value pairs to iterate over are the list name-value pairs with the
key being the name and the value being the value.
Since URLParams, returns an iterable object, using the spread operator instead of calling .entries will also yield entries per its spec:
const urlParams = new URLSearchParams('abc=foo&def=%5Basf%5D&xyz=5');
const params = Object.fromEntries(urlParams); // {abc: "foo", def: "[asf]", xyz: "5"}
Note: All values are automatically strings as per the URLSearchParams spec
Multiple same keys
As #siipe pointed out, strings containing multiple same-key values will be coerced into the last available value: foo=first_value&foo=second_value will in essence become: {foo: "second_value"}.
As per this answer: https://stackoverflow.com/a/1746566/1194694 there's no spec for deciding what to do with it and each framework can behave differently.
A common use case will be to join the two same values into an array, making the output object into:
{foo: ["first_value", "second_value"]}
This can be achieved with the following code:
const groupParamsByKey = (params) => [...params.entries()].reduce((acc, tuple) => {
// getting the key and value from each tuple
const [key, val] = tuple;
if(acc.hasOwnProperty(key)) {
// if the current key is already an array, we'll add the value to it
if(Array.isArray(acc[key])) {
acc[key] = [...acc[key], val]
} else {
// if it's not an array, but contains a value, we'll convert it into an array
// and add the current value to it
acc[key] = [acc[key], val];
}
} else {
// plain assignment if no special case is present
acc[key] = val;
}
return acc;
}, {});
const params = new URLSearchParams('abc=foo&def=%5Basf%5D&xyz=5&def=dude');
const output = groupParamsByKey(params) // {abc: "foo", def: ["[asf]", "dude"], xyz: 5}
One liner. Clean and simple.
const params = Object.fromEntries(new URLSearchParams(location.search));
For your specific case, it would be:
const str = 'abc=foo&def=%5Basf%5D&xyz=5';
const params = Object.fromEntries(new URLSearchParams(str));
console.log(params);
2023 One-Liner Approach
For the general case where you want to parse query params to an object:
Object.fromEntries(new URLSearchParams(location.search));
For your specific case:
Object.fromEntries(new URLSearchParams('abc=foo&def=%5Basf%5D&xyz=5'));
Split on & to get name/value pairs, then split each pair on =. Here's an example:
var str = "abc=foo&def=%5Basf%5D&xy%5Bz=5"
var obj = str.split("&").reduce(function(prev, curr, i, arr) {
var p = curr.split("=");
prev[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
return prev;
}, {});
Another approach, using regular expressions:
var obj = {};
str.replace(/([^=&]+)=([^&]*)/g, function(m, key, value) {
obj[decodeURIComponent(key)] = decodeURIComponent(value);
});
This is adapted from John Resig's "Search and Don’t Replace".
The proposed solutions I found so far do not cover more complex scenarios.
I needed to convert a query string like
https://random.url.com?Target=Offer&Method=findAll&filters%5Bhas_goals_enabled%5D%5BTRUE%5D=1&filters%5Bstatus%5D=active&fields%5B%5D=id&fields%5B%5D=name&fields%5B%5D=default_goal_name
into an object like:
{
"Target": "Offer",
"Method": "findAll",
"fields": [
"id",
"name",
"default_goal_name"
],
"filters": {
"has_goals_enabled": {
"TRUE": "1"
},
"status": "active"
}
}
OR:
https://random.url.com?Target=Report&Method=getStats&fields%5B%5D=Offer.name&fields%5B%5D=Advertiser.company&fields%5B%5D=Stat.clicks&fields%5B%5D=Stat.conversions&fields%5B%5D=Stat.cpa&fields%5B%5D=Stat.payout&fields%5B%5D=Stat.date&fields%5B%5D=Stat.offer_id&fields%5B%5D=Affiliate.company&groups%5B%5D=Stat.offer_id&groups%5B%5D=Stat.date&filters%5BStat.affiliate_id%5D%5Bconditional%5D=EQUAL_TO&filters%5BStat.affiliate_id%5D%5Bvalues%5D=1831&limit=9999
INTO:
{
"Target": "Report",
"Method": "getStats",
"fields": [
"Offer.name",
"Advertiser.company",
"Stat.clicks",
"Stat.conversions",
"Stat.cpa",
"Stat.payout",
"Stat.date",
"Stat.offer_id",
"Affiliate.company"
],
"groups": [
"Stat.offer_id",
"Stat.date"
],
"limit": "9999",
"filters": {
"Stat.affiliate_id": {
"conditional": "EQUAL_TO",
"values": "1831"
}
}
}
I compiled and adapted multiple solutions into one that actually works:
CODE:
var getParamsAsObject = function (query) {
query = query.substring(query.indexOf('?') + 1);
var re = /([^&=]+)=?([^&]*)/g;
var decodeRE = /\+/g;
var decode = function (str) {
return decodeURIComponent(str.replace(decodeRE, " "));
};
var params = {}, e;
while (e = re.exec(query)) {
var k = decode(e[1]), v = decode(e[2]);
if (k.substring(k.length - 2) === '[]') {
k = k.substring(0, k.length - 2);
(params[k] || (params[k] = [])).push(v);
}
else params[k] = v;
}
var assign = function (obj, keyPath, value) {
var lastKeyIndex = keyPath.length - 1;
for (var i = 0; i < lastKeyIndex; ++i) {
var key = keyPath[i];
if (!(key in obj))
obj[key] = {}
obj = obj[key];
}
obj[keyPath[lastKeyIndex]] = value;
}
for (var prop in params) {
var structure = prop.split('[');
if (structure.length > 1) {
var levels = [];
structure.forEach(function (item, i) {
var key = item.replace(/[?[\]\\ ]/g, '');
levels.push(key);
});
assign(params, levels, params[prop]);
delete(params[prop]);
}
}
return params;
};
A concise solution:
location.search
.slice(1)
.split('&')
.map(p => p.split('='))
.reduce((obj, pair) => {
const [key, value] = pair.map(decodeURIComponent);
obj[key] = value;
return obj;
}, {});
This is the simple version, obviously you'll want to add some error checking:
var obj = {};
var pairs = queryString.split('&');
for(i in pairs){
var split = pairs[i].split('=');
obj[decodeURIComponent(split[0])] = decodeURIComponent(split[1]);
}
For Node JS, you can use the Node JS API querystring:
const querystring = require('querystring');
querystring.parse('abc=foo&def=%5Basf%5D&xyz=5&foo=b%3Dar');
// returns the object
Documentation: https://nodejs.org/api/querystring.html
I found $.String.deparam the most complete pre built solution (can do nested objects etc.). Check out the documentation.
Another solution based on the latest standard of URLSearchParams (https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams)
function getQueryParamsObject() {
const searchParams = new URLSearchParams(location.search.slice(1));
return searchParams
? _.fromPairs(Array.from(searchParams.entries()))
: {};
}
Please note that this solution is making use of
Array.from (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from)
and _.fromPairs (https://lodash.com/docs#fromPairs) of lodash for the sake of simplicity.
It should be easy to create a more compatible solution since you have access to searchParams.entries() iterator.
I had the same problem, tried the solutions here, but none of them really worked, since I had arrays in the URL parameters, like this:
?param[]=5&param[]=8&othr_param=abc&param[]=string
So I ended up writing my own JS function, which makes an array out of the param in URI:
/**
* Creates an object from URL encoded data
*/
var createObjFromURI = function() {
var uri = decodeURI(location.search.substr(1));
var chunks = uri.split('&');
var params = Object();
for (var i=0; i < chunks.length ; i++) {
var chunk = chunks[i].split('=');
if(chunk[0].search("\\[\\]") !== -1) {
if( typeof params[chunk[0]] === 'undefined' ) {
params[chunk[0]] = [chunk[1]];
} else {
params[chunk[0]].push(chunk[1]);
}
} else {
params[chunk[0]] = chunk[1];
}
}
return params;
}
One of the simplest way to do this using URLSearchParam interface.
Below is the working code snippet:
let paramObj={},
querystring=window.location.search,
searchParams = new URLSearchParams(querystring);
//*** :loop to add key and values to the param object.
searchParams.forEach(function(value, key) {
paramObj[key] = value;
});
There is quite simple and incorrect answer with ES6:
console.log(
Object.fromEntries(new URLSearchParams(`abc=foo&def=%5Basf%5D&xyz=5`))
);
But this one line code do not cover multiple same keys, you have to use something more complicated:
function parseParams(params) {
const output = [];
const searchParams = new URLSearchParams(params);
// Set will return only unique keys()
new Set([...searchParams.keys()])
.forEach(key => {
output[key] = searchParams.getAll(key).length > 1 ?
searchParams.getAll(key) : // get multiple values
searchParams.get(key); // get single value
});
return output;
}
console.log(
parseParams('abc=foo&cars=Ford&cars=BMW&cars=Skoda&cars=Mercedes')
)
Code will generate follow structure:
[
abc: "foo"
cars: ["Ford", "BMW", "Skoda", "Mercedes"]
]
Using ES6, URL API and URLSearchParams API.
function objectifyQueryString(url) {
let _url = new URL(url);
let _params = new URLSearchParams(_url.search);
let query = Array.from(_params.keys()).reduce((sum, value)=>{
return Object.assign({[value]: _params.get(value)}, sum);
}, {});
return query;
}
ES6 one liner (if we can call it that way seeing the long line)
[...new URLSearchParams(location.search).entries()].reduce((prev, [key,val]) => {prev[key] = val; return prev}, {})
One simple answer with build in native Node module.(No third party npm modules)
The querystring module provides utilities for parsing and formatting URL query strings. It can be accessed using:
const querystring = require('querystring');
const body = "abc=foo&def=%5Basf%5D&xyz=5"
const parseJSON = querystring.parse(body);
console.log(parseJSON);
Pretty easy using the URLSearchParams JavaScript Web API,
var paramsString = "abc=foo&def=%5Basf%5D&xyz=5";
//returns an iterator object
var searchParams = new URLSearchParams(paramsString);
//Usage
for (let p of searchParams) {
console.log(p);
}
//Get the query strings
console.log(searchParams.toString());
//You can also pass in objects
var paramsObject = {abc:"forum",def:"%5Basf%5D",xyz:"5"}
//returns an iterator object
var searchParams = new URLSearchParams(paramsObject);
//Usage
for (let p of searchParams) {
console.log(p);
}
//Get the query strings
console.log(searchParams.toString());
##Useful Links
URLSearchParams - Web APIs | MDN
Easy URL Manipulation with URLSearchParams | Web
| Google Developers
NOTE: Not Supported in IE
There is no native solution that I'm aware of. Dojo has a built-in unserialization method if you use that framework by chance.
Otherwise you can implement it yourself rather simply:
function unserialize(str) {
str = decodeURIComponent(str);
var chunks = str.split('&'),
obj = {};
for(var c=0; c < chunks.length; c++) {
var split = chunks[c].split('=', 2);
obj[split[0]] = split[1];
}
return obj;
}
edit: added decodeURIComponent()
/**
* Parses and builds Object of URL query string.
* #param {string} query The URL query string.
* #return {!Object<string, string>}
*/
function parseQueryString(query) {
if (!query) {
return {};
}
return (/^[?#]/.test(query) ? query.slice(1) : query)
.split('&')
.reduce((params, param) => {
const item = param.split('=');
const key = decodeURIComponent(item[0] || '');
const value = decodeURIComponent(item[1] || '');
if (key) {
params[key] = value;
}
return params;
}, {});
}
console.log(parseQueryString('?v=MFa9pvnVe0w&ku=user&from=89&aw=1'))
see log
There's a lightweight library called YouAreI.js that's tested and makes this really easy.
YouAreI = require('YouAreI')
uri = new YouAreI('http://user:pass#www.example.com:3000/a/b/c?d=dad&e=1&f=12.3#fragment');
uri.query_get() => { d: 'dad', e: '1', f: '12.3' }
If you are using URI.js, you can use:
https://medialize.github.io/URI.js/docs.html#static-parseQuery
var result = URI.parseQuery("?foo=bar&hello=world&hello=mars&bam=&yup");
result === {
foo: "bar",
hello: ["world", "mars"],
bam: "",
yup: null
};
console.log(decodeURI('abc=foo&def=%5Basf%5D&xyz=5')
.split('&')
.reduce((result, current) => {
const [key, value] = current.split('=');
result[key] = value;
return result
}, {}))
This seems to be the best solution as it takes multiple parameters of the same name into consideration.
function paramsToJSON(str) {
var pairs = str.split('&');
var result = {};
pairs.forEach(function(pair) {
pair = pair.split('=');
var name = pair[0]
var value = pair[1]
if( name.length )
if (result[name] !== undefined) {
if (!result[name].push) {
result[name] = [result[name]];
}
result[name].push(value || '');
} else {
result[name] = value || '';
}
});
return( result );
}
something
paramsToJSON("x=1&x=2&x=3&y=blah");
console yields => {x: Array[3], y: "blah"} where x is an array as is proper JSON
I later decided to convert it to a jQuery plugin too...
$.fn.serializeURLParams = function() {
var result = {};
if( !this.is("a") || this.attr("href").indexOf("?") == -1 )
return( result );
var pairs = this.attr("href").split("?")[1].split('&');
pairs.forEach(function(pair) {
pair = pair.split('=');
var name = decodeURI(pair[0])
var value = decodeURI(pair[1])
if( name.length )
if (result[name] !== undefined) {
if (!result[name].push) {
result[name] = [result[name]];
}
result[name].push(value || '');
} else {
result[name] = value || '';
}
});
return( result )
}
something
$("a").serializeURLParams();
console yields => {x: Array[3], y: "blah"} where x is an array as is proper JSON
Now, the first will accept the parameters only but the jQuery plugin will take the whole url and return the serialized parameters.
Here's one I use:
var params = {};
window.location.search.substring(1).split('&').forEach(function(pair) {
pair = pair.split('=');
if (pair[1] !== undefined) {
var key = decodeURIComponent(pair[0]),
val = decodeURIComponent(pair[1]),
val = val ? val.replace(/\++/g,' ').trim() : '';
if (key.length === 0) {
return;
}
if (params[key] === undefined) {
params[key] = val;
}
else {
if ("function" !== typeof params[key].push) {
params[key] = [params[key]];
}
params[key].push(val);
}
}
});
console.log(params);
Basic usage, eg.
?a=aa&b=bb
Object {a: "aa", b: "bb"}
Duplicate params, eg.
?a=aa&b=bb&c=cc&c=potato
Object {a: "aa", b: "bb", c: ["cc","potato"]}
Missing keys, eg.
?a=aa&b=bb&=cc
Object {a: "aa", b: "bb"}
Missing values, eg.
?a=aa&b=bb&c
Object {a: "aa", b: "bb"}
The above JSON/regex solutions throw a syntax error on this wacky url:
?a=aa&b=bb&c=&=dd&e
Object {a: "aa", b: "bb", c: ""}
Here's my quick and dirty version, basically its splitting up the URL parameters separated by '&' into array elements, and then iterates over that array adding key/value pairs separated by '=' into an object. I'm using decodeURIComponent() to translate the encoded characters to their normal string equivalents (so %20 becomes a space, %26 becomes '&', etc):
function deparam(paramStr) {
let paramArr = paramStr.split('&');
let paramObj = {};
paramArr.forEach(e=>{
let param = e.split('=');
paramObj[param[0]] = decodeURIComponent(param[1]);
});
return paramObj;
}
example:
deparam('abc=foo&def=%5Basf%5D&xyz=5')
returns
{
abc: "foo"
def:"[asf]"
xyz :"5"
}
The only issue is that xyz is a string and not a number (due to using decodeURIComponent()), but beyond that its not a bad starting point.
//under ES6
const getUrlParamAsObject = (url = window.location.href) => {
let searchParams = url.split('?')[1];
const result = {};
//in case the queryString is empty
if (searchParams!==undefined) {
const paramParts = searchParams.split('&');
for(let part of paramParts) {
let paramValuePair = part.split('=');
//exclude the case when the param has no value
if(paramValuePair.length===2) {
result[paramValuePair[0]] = decodeURIComponent(paramValuePair[1]);
}
}
}
return result;
}
If you need recursion, you can use the tiny js-extension-ling library.
npm i js-extension-ling
const jsx = require("js-extension-ling");
console.log(jsx.queryStringToObject("a=1"));
console.log(jsx.queryStringToObject("a=1&a=3"));
console.log(jsx.queryStringToObject("a[]=1"));
console.log(jsx.queryStringToObject("a[]=1&a[]=pomme"));
console.log(jsx.queryStringToObject("a[0]=one&a[1]=five"));
console.log(jsx.queryStringToObject("http://blabla?foo=bar&number=1234"));
console.log(jsx.queryStringToObject("a[fruits][red][]=strawberry"));
console.log(jsx.queryStringToObject("a[fruits][red][]=strawberry&a[1]=five&a[fruits][red][]=cherry&a[fruits][yellow][]=lemon&a[fruits][yellow][688]=banana"));
This will output something like this:
{ a: '1' }
{ a: '3' }
{ a: { '0': '1' } }
{ a: { '0': '1', '1': 'pomme' } }
{ a: { '0': 'one', '1': 'five' } }
{ foo: 'bar', number: '1234' }
{
a: { fruits: { red: { '0': 'strawberry' } } }
}
{
a: {
'1': 'five',
fruits: {
red: { '0': 'strawberry', '1': 'cherry' },
yellow: { '0': 'lemon', '688': 'banana' }
}
}
}
Note: it's based on locutus parse_str function (https://locutus.io/php/strings/parse_str/).
FIRST U NEED TO DEFINE WHAT'S A GET VAR:
function getVar()
{
this.length = 0;
this.keys = [];
this.push = function(key, value)
{
if(key=="") key = this.length++;
this[key] = value;
this.keys.push(key);
return this[key];
}
}
Than just read:
function urlElement()
{
var thisPrototype = window.location;
for(var prototypeI in thisPrototype) this[prototypeI] = thisPrototype[prototypeI];
this.Variables = new getVar();
if(!this.search) return this;
var variables = this.search.replace(/\?/g,'').split('&');
for(var varI=0; varI<variables.length; varI++)
{
var nameval = variables[varI].split('=');
var name = nameval[0].replace(/\]/g,'').split('[');
var pVariable = this.Variables;
for(var nameI=0;nameI<name.length;nameI++)
{
if(name.length-1==nameI) pVariable.push(name[nameI],nameval[1]);
else var pVariable = (typeof pVariable[name[nameI]] != 'object')? pVariable.push(name[nameI],new getVar()) : pVariable[name[nameI]];
}
}
}
and use like:
var mlocation = new urlElement();
mlocation = mlocation.Variables;
for(var key=0;key<mlocation.keys.length;key++)
{
console.log(key);
console.log(mlocation[mlocation.keys[key]];
}
I needed to also deal with + in the query part of the URL (decodeURIComponent doesn't), so I adapted Wolfgang's code to become:
var search = location.search.substring(1);
search = search?JSON.parse('{"' + search.replace(/\+/g, ' ').replace(/&/g, '","').replace(/=/g,'":"') + '"}',
function(key, value) { return key===""?value:decodeURIComponent(value)}):{};
In my case, I'm using jQuery to get URL-ready form parameters, then this trick to build an object out of it and I can then easily update parameters on the object and rebuild the query URL, e.g.:
var objForm = JSON.parse('{"' + $myForm.serialize().replace(/\+/g, ' ').replace(/&/g, '","').replace(/=/g,'":"') + '"}',
function(key, value) { return key===""?value:decodeURIComponent(value)});
objForm.anyParam += stringToAddToTheParam;
var serializedForm = $.param(objForm);

Create an object based on file path string

Given the path "documents/settings/user"
How can this be made into a nested object
Result
{
documents : {
settings : {
user : {}
}
}
}
I can't think of how to reference each previous path
var dir = {};
var paths = "documents/settings/user".split('/')
for (var i = 0; i < paths.length; i++) {
var path = paths[i];
if(i === 0)
dir[path] = {};
//else
//dir[path[i-?]] = {};
}
This is actually done quite easily with .reduce().
var dir = {}
var paths = "documents/settings/user".split('/')
paths.reduce(function(dir, path) {
return dir[path] = {}
}, dir)
console.log(dir)
Because the first parameter is always the last value returned (or the first value provided), all we need to do is return the object being assigned to the current path. And because an assignment results in the value being assigned, we can use that as the expression of the return statement itself.
If needed, you can guard against empty path names due to adjacent separators.
var dir = {}
var paths = "documents///settings/user".split('/')
paths.reduce(function(dir, path) {
return path ? (dir[path] = {}) : dir
}, dir)
console.log(dir)
Yet another elegant solution to build an object with value:
const buildObjWithValue = (path, value = '') => {
const paths = path.split('.');
return paths.reduceRight((acc, item, index) => ({
[item]: index === paths.length - 1
? value
: acc
}), {});
}
For example buildObjWithValue('very.deep.lake', 'Baikal') gives us
{
very: {
deep: {
lake: 'Bailkal'
}
}
}
Objects are passed by reference. You can use this feature and do something like this.
Array.forEach
var paths = "documents/settings/user".split('/')
var r = {};
var _tmp = r;
paths.forEach(function(el){
_tmp[el] = {};
_tmp = _tmp[el];
});
console.log(r)
For
var paths = "documents/settings/user".split('/')
var r = {};
var _tmp = r;
for(var i=0; i<paths.length; i++){
_tmp = (_tmp[paths[i]] = {});
};
console.log(r)

Categories

Resources