How to normalize JS Object with special char property - javascript

Hey guys I'm working on a project and I'm getting an object that looks like this:
{
"userChoice[language]": "en",
"userChoice[responses][favColor]": "black",
"userChoice[responses][favCity]": "new york",
}
How do I normalize that? So I can access the properties that I need?
Thanks

Here is a es5 approach to normalize the object
function normalisedObject(object) {
var normalised = {};
for (var key in object) {
//regex to capture all [.*] groups
var matches = key.match(/\[(.*?)\]/g);
if (matches) {
var temp = normalised;
while (matches.length > 0) {
//get first key and replace []
var newKey = matches[0].replace(/\[|\]/g, "");
if (matches.length !== 1) {
//keep traverse if the left over keys are greater than 1
temp = temp[newKey] || (temp[newKey] = {}); //assign empty object
} else {
temp[newKey] = object[key];
}
//poll
matches.shift();
}
}
}
return normalised;
}
//example
const t1 = performance.now();
var object = {
"userChoice[language]": "en",
"userChoice[responses][favColor]": "black",
"userChoice[responses][favCity]": "new york"
};
var normalised = normalisedObject(object);
const t2 = performance.now();
console.log(`Operation took ${t2 - t1}ms`);
console.log(normalised);

When an object's keys don't allow you to use simple dot notation, ie obj.property, you can use square-bracket notation instead, eg
const lang = obj["userChoice[language]"]
But I have a feeling you'd actually like to transform that object into something resembling this
{
userChoice: {
language: "en",
responses: {
favColor: "black",
favCity: "new york"
}
}
}
If that's the case, you need to reduce the object entries (key / value pairs) to a new object, parsing out the key paths and building new, inner objects as you go
const obj = {
"userChoice[language]": "en",
"userChoice[responses][favColor]": "black",
"userChoice[responses][favCity]": "new york",
}
const t1 = performance.now()
const normalised = Object.entries(obj).reduce((c, [ key, val ]) => {
// get the key parts as a path array
const path = key.replace(/]/g, "").split(/\[/)
// pop the last property for assigning the value later
const prop = path.pop()
// determine the inner-most object by path
const inner = path.reduce((o, k) => {
// create a new object if it doesn't exist
return o[k] ?? (o[k] = {})
}, c)
// assign the value
inner[prop] = val
return c
}, {})
const t2 = performance.now()
console.info(normalised)
console.log(`Operation took ${t2 - t1}ms`)
.as-console-wrapper { max-height: 100% !important; }
Note that if you start throwing any array properties in, eg userChoice[foo][0], this won't work.

Related

Convert array of structured string into multi-level object in Javascript [duplicate]

This question already has answers here:
Create an object based on file path string
(3 answers)
Closed 1 year ago.
I have an array of structured strings with have connection | as a format which self-divided into levels. I want to convert it into a structured object with multiple levels.
Input:
[
"clothes|tshirt|tshirt-for-kids",
"clothes|coat|raincoat",
"clothes|coat|leather-coat",
"clothes|tshirt|tshirt-for-men",
"clothes|tshirt|tshirt-for-men|luxury-tshirt",
]
Expected output:
{
clothes: {
tshirt: {
tshirt-for-kids: {},
tshirt-for-men: {
luxury-tshirt: {}
}
},
coat: {
raincoat: {}
leather-coat: {}
}
}
}
Very simple task - just enumerate the array and create the relevant object keys:
var myArray = [
"clothes|tshirt|tshirt-for-kids",
"clothes|coat|raincoat",
"clothes|coat|leather-coat",
"clothes|tshirt|tshirt-for-men",
"clothes|tshirt|tshirt-for-men|luxury-tshirt",
]
var result = {}, levels, current, temp;
while(myArray.length > 0)
{
levels = myArray.pop().split('|');
temp = result;
while(levels.length > 0)
{
current = levels.shift();
if(!(current in temp)) temp[current] = {};
temp = temp[current];
}
}
console.log(result);
You could try this:
const input = [
"clothes|tshirt|tshirt-for-kids",
"clothes|coat|raincoat",
"clothes|coat|leather-coat",
"clothes|tshirt|tshirt-for-men",
"clothes|tshirt|tshirt-for-men|luxury-tshirt",
];
function convertStrToObject(str, sep, obj) {
const sepIndex = str.indexOf(sep);
if (sepIndex == -1) {
obj[str] = obj[str] || {};
} else {
const key = str.substring(0, sepIndex);
obj[key] = obj[key] || {};
convertStrToObject(str.substring(sepIndex + 1), sep, obj[key]);
}
}
const all = {};
for (let i = 0; i < input.length; ++i) {
convertStrToObject(input[i], "|", all);
}
console.log(all);
Assuming you intend to collect properties, all having an empty object as leaf node.
// input
const input = [
"clothes|tshirt|tshirt-for-kids",
"clothes|coat|raincoat",
"clothes|coat|leather-coat",
"clothes|tshirt|tshirt-for-men",
"clothes|tshirt|tshirt-for-men|luxury-tshirt",
];
// Here, we collect the properties
const out = {};
// map the input array, splitting each line at |
input.map(i => i.split('|'))
.filter(a => a.length > 0) // lets not entertain empty lines in input
.forEach(a => { // process each array of property names
// start at outermost level
let p = out;
// iterate properties
for(const v of a){
// create a property if it is not already there
if(!p.hasOwnProperty(v)){
p[v] = {};
}
// move to the nested level
p = p[v];
}
});
// lets see what we have created
console.log(out);
A number of solutions have been suggested already, but I'm surprised none involves reduce() - which would seem the more idiomatic solution to me.
var array = [
"clothes|tshirt|tshirt-for-kids",
"clothes|coat|raincoat",
"clothes|coat|leather-coat",
"clothes|tshirt|tshirt-for-men",
"clothes|tshirt|tshirt-for-men|luxury-tshirt",
]
var object = array.reduce(function (object, element) {
var keys = element.split("|")
keys.reduce(function (nextNestedObject, key) {
if (!nextNestedObject[key]) nextNestedObject[key] = {}
return nextNestedObject[key]
}, object)
return object
}, {})
console.log(object)
One Liner With eval
Used eval to evaluate strings like the following:
'o["clothes"]??={}'
'o["clothes"]["tshirt"]??={}'
'o["clothes"]["tshirt"]["tshirt-for-kids"]??={}'
const
data = ["clothes|tshirt|tshirt-for-kids", "clothes|coat|raincoat", "clothes|coat|leather-coat", "clothes|tshirt|tshirt-for-men", "clothes|tshirt|tshirt-for-men|luxury-tshirt"],
arr = data.map((d) => d.split("|")),
res = arr.reduce((r, a) => (a.forEach((k, i) => eval(`r["${a.slice(0, i + 1).join('"]["')}"]??={}`)), r), {});
console.log(res)
One Liner Without eval
const
data = ["clothes|tshirt|tshirt-for-kids", "clothes|coat|raincoat", "clothes|coat|leather-coat","clothes|tshirt|tshirt-for-men", "clothes|tshirt|tshirt-for-men|luxury-tshirt"],
res = data.reduce((r, d) => (d.split("|").reduce((o, k) => (o[k] ??= {}, o[k]), r), r), {});
console.log(res)

How to compare 2 array and add key if don't have

Supposed that I have two arrays to compare:
var arrOriginal={currency1: 1234, currency2: 2345};
var arrToCompare={currency1: 123};
I was trying to find the difference between the two.
Example result:
var result={currency1: 1111, currency2: 2345}
What I've tried:
Using for .. in and then substract.
for (var key in this.arrOriginal) {
this.result.push({
currency: key,
amount: format.formatCurrency(this.arrToCompare[key] - this.arrOriginal[key])
});
}
But it won't show the currency key which the second array don't have.
Any help is appreciated.
You could take a default value of zero for undefined properties of the object.
From inside to out:
Get entries from the object as an array of key/value pairs.
Map this entries and get an object with the key and a value of the difference of value and the corresponding value of the other object or zero.
Spread the object as parameters for Object.assign.
And get a new object with all objects from mapping.
var original = { currency1: 1234, currency2: 2345 },
deltas = { currency1: 123 },
result = Object.assign(
{},
...Object.entries(original).map(([k, v]) => ({ [k]: v - (deltas[k] || 0)}))
);
console.log(result);
A very simple approach is looping over the keys of arrOriginal, If that key is present in arrToCompare then substract that value else do not substract.
var arrOriginal={currency1: 1234, currency2: 2345};
var arrToCompare={currency1: 123};
var out = {}
for(var key in arrOriginal){
out[key] = arrOriginal[key] - (arrToCompare[key] || 0)
}
console.log(out)
You can use Object.entries and reduce
var arrOriginal={currency1: 1234, currency2: 2345};
var arrToCompare={currency1: 123};
let final = Object.entries(arrOriginal).reduce((op,[key,value])=>{
op[key] = value - (arrToCompare[key] || 0)
return op
},{})
console.log(final)
A very easy to understand approach would be to have an if-else statement with three branches that check if the key exists in both or either one of the original objects:
const original = { currency1: 1234, currency2: 2345 }
const toCompare = { currency1: 123 }
const result = {}
const allKeys = new Set(Object.keys(original).concat(Object.keys(toCompare)))
allKeys.forEach(k => {
if (k in original && k in toCompare) {
result[k] = original[k] - toCompare[k]
} else if (k in original) {
result[k] = original[k]
} else {
result[k] = toCompare[k]
}
})
console.log(result)

Javascript building an object directly

I have these values:
let buffer = {};
let value = 'value';
let x = 1;
let y = 2;
This is what I want to do:
buffer[x][y].value = value;
This is what I need to do, in order for it to work:
buffer[x] = {};
buffer[x][y] = {};
buffer[x][y].value = value;
My guess is that there is a better, maybe built in way to create an object like this in one step instead of three.
My guess is that there is a better, maybe built in way to create an object like this in one step instead of three.
What you have is fine, but you can also do it with computed properties (I'm assuming you need the values from variables) and shorthand property notation (for value):
let value = 'value';
let x = 1;
let y = 2;
let buffer = {
[x]: {
[y]: {
value
}
}
};
console.log(buffer);
If you want to create nested object based on a set of keys, you could use reduceRight. Use the rest parameters syntax to get the value and the array of paths as separate variables. Set the value as the initialValue parameter and add a level of nesting in each iteration
const create = (value, ...paths) => paths.reduceRight((r, k) => ({ [k]: r }), value)
console.log(create("final value", "1", "2", "value"))
console.log(create("final value", "any", "number", "of", "keys"))
Like this?
const buffer = {
1: {
2: {
value: 'value'
}
}
};
If x and y are already defined, then use computed properties:
let x = 1;
let y = 2;
const buffer = {
[x]: {
[y]: {
value: 'value'
}
}
};
You could reduce the given keys and assign the value at the last step.
const
setValue = (object, path, value) =>
path.reduce((o, k, i, { length }) => o[k] = i + 1 === length
? value
: o[k] || {}, object);
let buffer = {};
let value = 'value';
let x = 1;
let y = 2;
setValue(buffer, [x, y, value], value);
console.log(buffer);

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);

Convert set of object's into array item

I have several objects like this:
{'id[0]': 2}
{'url[0]': 11}
{'id[1]': 3}
{'url[1]': 14}
And I want to get something like this:
[{id:2, url:11}, {id:3, url:14}]
Also I have lodash in my project. Maybe lodash have some method for this?
You could use a regular expression for the keys and create a new object if necessary. Then assign the value to the key.
var data = [{ 'id[0]': 2 }, { 'url[0]': 11 }, { 'id[1]': 3 }, { 'url[1]': 14 }],
result = [];
data.forEach(function (a) {
Object.keys(a).forEach(function (k) {
var keys = k.match(/^([^\[]+)\[(\d+)\]$/);
if (keys.length === 3) {
result[keys[2]] = result[keys[2]] || {};
result[keys[2]][keys[1]] = a[k];
}
});
});
console.log(result);
This is an ES6 solution based on #NinaScholz solution.
I assume that the objects have only one property each, like the ones presented in the question.
Combine the array of objects to one large object using Object#assign, and convert to entries with Object.entries.
Iterate the array using Array#reduce.
Extract the original key an value from each entry using array
destructuring.
Extract the wanted key and index using a regex and array
destructuring.
Then create/update the new object at the index using object spread.
const data = [{ 'id[0]': 2 }, { 'url[0]': 11 }, { 'id[1]': 3 }, { 'url[1]': 14 }];
// combine to one object, and convert to entries
const result = Object.entries(Object.assign({}, ...data))
// extract the original key and value
.reduce((r, [k, value]) => {
// extract the key and index while ignoring the full match
const [, key, index] = k.match(/^([^\[]+)\[(\d+)\]$/);
// create/update the object at the index
r[index] = {...(r[index] || {}), [key]: value };
return r;
}, []);
console.log(result);
var arr = [{'id[0]': 2},
{'url[0]': 11},
{'id[1]': 3},
{'url[1]': 14}];
var result = [];
arr.forEach(function(e, i, a){
var index = +Object.keys(e)[0].split('[')[1].split(']')[0];//get the number inside []
result[index] = result[index] || {}; //if item is undefined make it empty object
result[index][Object.keys(e)[0].split('[')[0]] = e[Object.keys(e)[0]];//add item to object
})
console.log(result);
You can use for loop, .filter(), RegExp constructor with parameter "\["+i+"\]" where i is current index, Object.keys(), .reduce(), .replace() with RegExp /\[\d+\]/
var obj = [{
"id[0]": 2
}, {
"url[0]": 11
}, {
"id[1]": 3
}, {
"url[1]": 14
}];
var res = [];
for (var i = 0; i < obj.length / 2; i++) {
res[i] = obj.filter(function(o) {
return new RegExp("\[" + i + "\]").test(Object.keys(o))
})
.reduce(function(obj, o) {
var key = Object.keys(o).pop();
obj[key.replace(/\[\d+\]/, "")] = o[key];
return obj
}, {})
}
console.log(res);

Categories

Resources