How to convert array EX-["lat","long","abc","def","abcc","deef",]
into [lat,long | abc,def | abcc,deef] in javascript.
I am facing issue with distance matrix Api...
Below is my code
export async function getStoreDistance(locationDetails) {
destinationRequest = [];
let destinationRequest = locationDetails.destinations.map(location => {
console.log('destreqlocation', location);
return `${location.lat},${location.long} `;
});
return await axios
.get(
`https://maps.googleapis.com/maps/api/distancematrix/json?units=imperial
&origins=${locationDetails.origins.map(function(locationDetail) {
return locationDetail;
})}
&destinations=${destinationRequest}&key=**********`,
)
.then(function(response) {
// handle success
// return response;
})
.catch(function(error) {
// handle error
return error;
});
}
My solution to the problem.
const so1 = ["lat","long","abc","def","abcc","deef"]
let result = so1
.map((item, id, array) => ((id % 2) !== 0 && id !== (array.length - 1)) ? item + '|' : (id !== (array.length - 1)) ? item + '&' : item)
.join('')
.replace(/&/g, ',')
console.log( result )
console.log( `[${result}]` )
Try something like below
input = ["lat", "long", "abc", "def", "abcc", "deef"];
const [lat, long, ...rest] = input;
res = rest.reduce((acc, val, index) => {
if(index % 2 === 0) acc.push([]);
acc[acc.length -1].push(val);
return acc;
}, []);
resFinal = [[lat, long], ...res];
console.log(resFinal);
resFinalStr = resFinal.reduce((acc, val, index)=> {
if(index !== resFinal.length -1){
acc+=(val.join(",")) + "|";
}else{
acc += val.join(",")
}
return acc;
}, "")
console.log(resFinalStr)
console.log(`[${resFinalStr}]`)
An old-fashioned for loop should do the job fine:
function getStoreDistance(locationDetails) {
destinationRequest = locationDetails[0] || "";
for (let i = 1; i < locationDetails.length; i++) {
destinationRequest += (i % 2 ? "," : " | ") + locationDetails[i];
}
return destinationRequest;
}
// Demo
let response = ["lat","long","abc","def","abcc","deef"];
console.log(getStoreDistance(response));
Related
I am trying to parse pdf datas asynchronously, then populate a JS object with the content of the pdf file, and then return it in a Promise.
I am using the "pdfreader" module and its method parseFileItems()
async function parsePdfDatas(filePath){
var output = {};
var reader = new pdfreader.PdfReader();
await reader.parseFileItems(filePath, function(err, item) {
// item treatment populating output Object
});
return output;
}
parsePdfDatas("./****.pdf").then( function(output) {
console.log(output);
});
The await statement doesn't work, anybody get an idea ?
EDIT
After xMayank answer, i tried as follows, which doesn't work neither:
const fs = require('fs');
var pdfreader = require("pdfreader");
var row = {
id: "",
muban: "",
get mID() {
this.id.slice(6,8);
},
tambon: "",
get tID() {
this.id.slice(4,6);
},
amphoe: "",
get aID() {
this.id.slice(2,4);
},
changwat: "",
get cID() {
this.id.slice(0,2);
}
}
function parsePdfDatas(filePath){
return new Promise(function(resolve, reject){
var output = {};
var reader = new pdfreader.PdfReader();
reader.parseFileItems(filePath, function(err, item) {
if(item && item.text && item.text.match(/^-{1,3}[0-9]{1,4}-{1,3}$/) === null && item.y != 2.887){
if(item.x === 2.388){
// If the row object contains a muban entry, we push it at the end of output
if(row.id !== ""){
//console.log(row);
output[row.id] = {mName : row.muban, tName : row.tambon, aName : row.amphoe, cName : row.changwat};
}
// new line, row object reinitialization
row.id = row.muban = row.tambon = row.amphoe = row.changwat = "";
}
// correction for ่ ้
if(item.R[0].T === "%E0%B8%BD") item.text = "่";
if(item.R[0].T === "%E0%B8%BE") item.text = "้";
if(item.x >= 2.388 && item.x < 11.303)
row.id += item.text;
else if(item.x >= 11.303 && item.x < 17.969)
row.muban += item.text;
else if(item.x >= 17.969 && item.x < 23.782)
row.tambon += item.text;
else if(item.x >= 23.782 && item.x < 29.698)
row.amphoe += item.text;
else if(item.x >= 29.698)
row.changwat += item.text;
console.log(item.R[0].T + " -> " + item.text);
//console.log(item.text + " : x = " + item.x + " | y = " + item.y);
}
});
resolve(output);
});
}
parsePdfDatas("./files/mubans0.pdf").then((output) => {
console.log(output);
});
Works fine.
const { PdfReader } = require("pdfreader");
function readFile(file){
return new Promise(function(resolve, reject){
new PdfReader().parseFileItems(file, function(err, item) {
if (err) reject(err);
else if (!item) resolve();
else if (item.text) resolve(item.text);
});
})
}
readFile("./****.pdf")
.then(result =>{
console.log("Here", result)
})
Following is my code, which is working fine in most scenarios except in case of leading Zeros. It should preserve trailing zeros like -001 + 1 = 002
Code -
function incrementString (str) {
if(str === '') return "1";
if(!str.slice(-1).match(/\d/)) return `${str}1`;
const replacer = x => {
// Check if number
return (parseInt(x) + 1).toString();
}
return str.replace(/\d+/g, replacer )
}
// Return foobar2 which is correct
console.log(incrementString("foobar1"))
// Return foobar100 which is correct
console.log(incrementString("foobar099"))
// Return foobar2 which is incorrect, is should be foobar002
console.log(incrementString("foobar001"))
// Return foobar1 which is incorrect, is should be foobar001
console.log(incrementString("foobar000"))
// Return foobar101 which is incorrect, is should be foobar0101
console.log(incrementString("foobar0100"))
You may use this regex soluton:
function incrementString (str) {
if(str === '') return "1";
if(!str.slice(-1).match(/\d/)) return `${str}1`;
const replacer = (m, g1, g2) => {
// Check if number
var nn = (g1?g1:"") + (parseInt(g2) + 1).toString()
return nn.slice(-1 * m.length)
}
return str.replace(/(0*)(\d+)/g, replacer )
}
// Return foobar2
console.log(incrementString("foobar1"))
// Return foobar100
console.log(incrementString("foobar099"))
// Return foobar002
console.log(incrementString("foobar001"))
// Return foobar001
console.log(incrementString("foobar000"))
// Return foobar0101
console.log(incrementString("foobar0100"))
// Return foobar01000
console.log(incrementString("foobar00999"))
// Return foobar010
console.log(incrementString("foobar009"))
Everything seems to be perfect, you need to only handle the regex part of the leading zeroes in your replacer function.Below is the updated code for the same.
function incrementString(str) {
if (str === '')
return "1";
if (!str.slice(-1).match(/\d/)) {
return `${str}1`;
}
const replacer = x => {
var leadingZerosMatched = x.match(/^0+/);
var incrementedNumber = (parseInt(x) + 1).toString();
var leadingZeroes;
if (leadingZerosMatched && incrementedNumber.length < x.length) {
leadingZeroes = leadingZerosMatched[0];
if(leadingZeroes.length === x.length) {
leadingZeroes = leadingZeroes.slice(0, leadingZeroes.length-1)
}
}
return leadingZeroes ? leadingZeroes + incrementedNumber : incrementedNumber;
}
return str.replace(/\d+/g, replacer)
}
You could split your string from digits and use padStart after increment to preserve leading 0:
const incrementString = (str) => {
const [chars, nums] = str.split(/(\d+)/)
return [
...chars,
String(Number(nums) + 1)
.padStart(nums.length, '0')
].join('')
}
console.log(incrementString("foobar1"))
console.log(incrementString("foobar099"))
console.log(incrementString("foobar001"))
console.log(incrementString("foobar000"))
console.log(incrementString("foobar0100"))
function incrementString (str) {
let [
openingPartial,
terminatingInt
] = str.split(/(\d*)$/);
if (terminatingInt) {
const incrementedInt = String(parseInt(terminatingInt, 10) + 1);
const leadingZeroCount = (terminatingInt.length - incrementedInt.length);
if (leadingZeroCount >= 1) {
terminatingInt = Array(leadingZeroCount).fill("0").concat(incrementedInt).join('');
} else {
terminatingInt = incrementedInt;
}
} else {
terminatingInt = '1';
}
return `${ (openingPartial || '') }${ terminatingInt }`;
}
// Should return 'foo_003_bar1'.
console.log(incrementString("foo_003_bar"));
// Should return 'foo_003_bar_01'.
console.log(incrementString("foo_003_bar_00"));
// Should return 'foobar1'.
console.log(incrementString("foobar"));
// Should return 'foobar2'.
console.log(incrementString("foobar1"));
// Should return 'foobar100'.
console.log(incrementString("foobar099"));
// Should return 'foobar002'.
console.log(incrementString("foobar001"));
// Should return 'foobar001'.
console.log(incrementString("foobar000"));
// Should return 'foobar0101'.
console.log(incrementString("foobar0100"));
.as-console-wrapper { max-height: 100%!important; top: 0; }
I have an array that is arranged in the following:
let example_arr =
[
"backend",
[
"#host#www.example.com",
"#port#80"
],
"endpoints",
[
"endpoint",
[
"#external#/foo/bar/cat/mom/",
"#internal#/banana/",
"params",
[
"param",
[
"#sourceValue#acis.usermove.md"
]
]
],
]
];
This array needs to be parsed into a string that would look like this:
"
<backend host="www.example.com" port="80" />
<endpoints>
<endpoint external="/foo/bar/cat/mom/" internal="/banana/"/>
<params>
<param sourceValue="acis.usermove.md" />
</params>
</endpoint>
</endpoints>
"
I have been working at this problem for awhile, but have scrapped attempt after attempt. Here is my latest try:
let str_answer = helper_string_builder(example_arr, 3);
function helper_string_builder(xml_list_data, depth) {
let string_builder = "";
for (let i = 0; i < xml_list_data.length; i++) {
if (Array.isArray(xml_list_data[i]) !== true) {
if (xml_list_data[i].charAt(0) !== "#" && xml_list_data[i].length > 1) {
if(typeof xml_list_data[i + 1] === "undefined"){
continue;
}
string_builder += " ".repeat(depth) + "<" + xml_list_data[i] + ">\n";
for (let j = 0; j < xml_list_data[i + 1].length; j++) {
if (j === 0) {
string_builder += " ".repeat(depth + 3)
}
string_builder +=
value_reader("name", xml_list_data[i + 1][j]) +
"=\"" +
value_reader("content", xml_list_data[i + 1][j]) +
"\" ";
}
string_builder += "\n" + " ".repeat(depth) + "</" + xml_list_data[i] + ">\n";
}
console.log(string_builder);
} else {
string_builder += helper_string_builder(xml_list_data[i], depth + 3);
}
}
return string_builder;
}
function value_reader(mode, str) {
str = str + '';
if (str.substring(0, 1) != '#') {
return 'ERROR';
}
if (mode === "name") {
let start_pos = str.indexOf('#') + 1;
let end_pos = str.indexOf('#', start_pos);
let name_cutout = str.substring(start_pos, end_pos);
return name_cutout;
} else if (mode === "content") {
let start_pos = str.indexOf('#') + 1;
let end_pos = str.indexOf('#', start_pos);
let content_cutout = str.substring(end_pos + 1);
return content_cutout;
} else {
throw new Error("Valid \'mode\' not passed in");
}
}
Unsure how to continue with solving this, I was wondering if anyone can help me. Thanks!
Your input format is quite tricky, so I guess my main advice is to go trough the tree twice:
First, create an easier-to-work-with tree of Element objects: each object has a tagName property, an attributes array, and a children array.
The attributes array can contain Attribute instances, which are { key, value } pairs
The children array can contain Element instances.
Then, loop over your tree and return strings for each Element
Reformating the data
The code got a bit messy, but I (think I) reused most of your logic:
If it's a string and starts with "#", it's an attribute
If it's a string and not an attribute, it's a new Element
If it's an array, the array contains the contents of the previously made Element
// Recursively create a tree of elements with attributes and children
const partition = xs => xs.reduce(
([lastPart, ...parts], x) => {
switch(getType(x)) {
case Element:
return [
Element(x),
...(lastPart ? [lastPart] : []),
...parts
];
case Array:
const attrsUntil = x.findIndex(Attribute.isNoAttribute);
const attrs = attrsUntil !== -1
? x.slice(0, attrsUntil)
: x;
const children = attrsUntil === -1
? []
: x.slice(attrsUntil);
return [ Element(
lastPart.tagName,
attrs.map(Attribute.fromString),
partition(children)
), ...parts];
default:
return [ lastPart, ...parts ];
}
},
[]
).reverse()
const getType = x =>
Array.isArray(x) ? Array
: Attribute.isAttribute(x) ? Attribute
: Element;
// Some utility models & methods:
const Attribute = (key, value) =>
({ key, value });
Attribute.isAttribute = (str) =>
str.charAt && str.charAt(0) === "#";
Attribute.isNoAttribute = x => !Attribute.isAttribute(x);
Attribute.fromString = str => Attribute(
...str.split("#").slice(1)
);
const Element = (tagName, attributes = [], children = []) =>
({ tagName, attributes, children });
let example_arr =
[
"backend",
[
"#host#www.example.com",
"#port#80"
],
"endpoints",
[
"endpoint",
[
"#external#/foo/bar/cat/mom/",
"#internal#/banana/",
"params",
[
"param",
[
"#sourceValue#acis.usermove.md"
]
]
],
]
];
console.log(
partition(example_arr)
);
From tree to string
With the new format, the string formatting can be implemented separately like so:
// The formatted tree:
const input = [{tagName:"backend",attributes:[{key:"host",value:"www.example.com"},{key:"port",value:"80"}],children:[]},{tagName:"endpoints",attributes:[],children:[{tagName:"endpoint",attributes:[{key:"external",value:"/foo/bar/cat/mom/"},{key:"internal",value:"/banana/"}],children:[{tagName:"params",attributes:[],children:[{tagName:"param",attributes:[{key:"sourceValue",value:"acis.usermove.md"}],children:[]}]}]}]}];
// A helper to prefix with an indentation
const indented = (n, str) => `${" ".repeat(n)}${str}`;
// This renders a self closing tag
const renderEmptyEl = (el, indent) => indented(
indent,
`<${el.tagName} ${renderAttributes(el)} />`
);
// This renders a tag that has children
const renderParentEl = (el, indent) => [
indented(indent, `<${el.tagName} ${renderAttributes(el)}>`),
...el.children.map(c => renderEl(c, indent + 2)),
indented(indent, `</${el.tagName}>`)
].join("\n");
const renderEl = (el, indent) => el.children.length
? renderParentEl(el, indent) : renderEmptyEl(el, indent);
// Render the attribute values
const renderAttributes = ({ attributes }) => attributes
.map(({ key, value }) => `${key}="${value}"`)
.join(" ");
// Entry point for an array of nodes not inside another Element
const renderTree = (nodes) =>
nodes.map(n => renderEl(n, 0)).join("\n");
console.log(renderTree(input));
I would like to list all paths of object that lead to leafs
Example:
var obj = {
a:"1",
b:{
foo:"2",
bar:3
},
c:[0,1]
}
Result:
"a","b.foo","b.bar", "c[0]","c[1]"
I would like to find simple and readable solution, best using lodash.
Here is a solution that uses lodash in as many ways as I can think of:
function paths(obj, parentKey) {
var result;
if (_.isArray(obj)) {
var idx = 0;
result = _.flatMap(obj, function (obj) {
return paths(obj, (parentKey || '') + '[' + idx++ + ']');
});
}
else if (_.isPlainObject(obj)) {
result = _.flatMap(_.keys(obj), function (key) {
return _.map(paths(obj[key], key), function (subkey) {
return (parentKey ? parentKey + '.' : '') + subkey;
});
});
}
else {
result = [];
}
return _.concat(result, parentKey || []);
}
Edit: If you truly want just the leaves, just return result in the last line.
Doesn't use lodash, but here it is with recursion:
var getLeaves = function(tree) {
var leaves = [];
var walk = function(obj,path){
path = path || "";
for(var n in obj){
if (obj.hasOwnProperty(n)) {
if(typeof obj[n] === "object" || obj[n] instanceof Array) {
walk(obj[n],path + "." + n);
} else {
leaves.push(path + "." + n);
}
}
}
}
walk(tree,"tree");
return leaves;
}
Based on Nick answer, here is a TS / ES6 imports version of the same code
import {isArray,flatMap,map,keys,isPlainObject,concat} from "lodash";
// See https://stackoverflow.com/a/36490174/82609
export function paths(obj: any, parentKey?: string): string[] {
var result: string[];
if (isArray(obj)) {
var idx = 0;
result = flatMap(obj, function(obj: any) {
return paths(obj, (parentKey || '') + '[' + idx++ + ']');
});
} else if (isPlainObject(obj)) {
result = flatMap(keys(obj), function(key) {
return map(paths(obj[key], key), function(subkey) {
return (parentKey ? parentKey + '.' : '') + subkey;
});
});
} else {
result = [];
}
return concat(result, parentKey || []);
}
Feeding that object through this function should do it I think.
recursePaths: function(obj){
var result = [];
//get keys for both arrays and objects
var keys = _.map(obj, function(value, index, collection){
return index;
});
//Iterate over keys
for (var key in keys) {
//Get paths for sub objects
if (typeof obj[key] === 'object'){
var paths = allPaths(obj[key]);
for (var path in paths){
result.push(key + "." + path);
}
} else {
result.push(key);
}
}
return result;
}
Here is my function. It generates all possible paths with dot notation, assuming there are no property names containing spaces
function getAllPathes(dataObj) {
const reducer = (aggregator, val, key) => {
let paths = [key];
if(_.isObject(val)) {
paths = _.reduce(val, reducer, []);
paths = _.map(paths, path => key + '.' + path);
}
aggregator.push(...paths);
return aggregator;
};
const arrayIndexRegEx = /\.(\d+)/gi;
let paths = _.reduce(dataObj, reducer, []);
paths = _.map(paths, path => path.replace(arrayIndexRegEx, '[$1]'));
return paths;
}
Here's my solution. I only did it because I felt the other solutions used too much logic. Mine does not use lodash since I don't think it would add any value. It also doesn't make array keys look like [0].
const getAllPaths = (() => {
function iterate(path,current,[key,value]){
const currentPath = [...path,key];
if(typeof value === 'object' && value != null){
return [
...current,
...iterateObject(value,currentPath)
];
}
else {
return [
...current,
currentPath.join('.')
];
}
}
function iterateObject(obj,path = []){
return Object.entries(obj).reduce(
iterate.bind(null,path),
[]
);
}
return iterateObject;
})();
If you need one where the keys are indexed using [] then use this:
const getAllPaths = (() => {
function iterate(path,isArray,current,[key,value]){
const currentPath = [...path];
if(isArray){
currentPath.push(`${currentPath.pop()}[${key}]`);
}
else {
currentPath.push(key);
}
if(typeof value === 'object' && value != null){
return [
...current,
...iterateObject(value,currentPath)
];
}
else {
return [
...current,
currentPath.join('.')
];
}
}
function iterateObject(obj,path = []){
return Object.entries(obj).reduce(
iterate.bind(null,path,Array.isArray(obj)),
[]
);
}
return iterateObject;
})();
const allEntries = (o, prefix = '', out = []) => {
if (_.isObject(o) || _.isArray(o)) Object.entries(o).forEach(([k, v]) => allEntries(v, prefix === '' ? k : `${prefix}.${k}`, out));
else out.push([prefix, o]);
return out;
};
Array are returned as .0 or .1 that are compatible with _.get of lodash
const getAllPaths = (obj: object) => {
function rKeys(o: object, path?: string) {
if (typeof o !== "object") return path;
return Object.keys(o).map((key) =>
rKeys(o[key], path ? [path, key].join(".") : key)
);
}
return rKeys(obj).toString().split(",").filter(Boolean) as string[];
};
const getAllPaths = (obj) => {
function rKeys(o, path) {
if (typeof o !== "object") return path;
return Object.keys(o).map((key) =>
rKeys(o[key], path ? [path, key].join(".") : key)
);
}
return rKeys(obj).toString().split(",").filter(Boolean);
};
const test = {
a: {
b: {
c: 1
},
d: 2
},
e: 1
}
console.log(getAllPaths(test))
I need convert json object to url form like: "parameter=12&asd=1"
I done with this:
var data = {
'action':'actualiza_resultado',
'postID': 1,
'gl': 2,
'gl2' : 3
};
var string_=JSON.stringify(data);
string_=string_.replace(/{/g, "");
string_=string_.replace(/}/g, "");
string_=string_.replace(/:/g, "=")
string_=string_.replace(/,/g, "&");
string_=string_.replace(/"/g, "");
But i wonder if there any function in javascript or in JSON object to do this?
Use the URLSearchParams interface, which is built into browsers and Node.js starting with version 10, released in 2018.
const myParams = {'foo': 'hi there', 'bar': '???'};
const u = new URLSearchParams(myParams).toString();
console.log(u);
Old answer: jQuery provides param that does exactly that. If you don't use jquery, take at look at the source.
Basically, it goes like this:
url = Object.keys(data).map(function(k) {
return encodeURIComponent(k) + '=' + encodeURIComponent(data[k])
}).join('&')
Using ES6 syntax:
var data = {
'action':'actualiza_resultado',
'postID': 1,
'gl': 2,
'gl2' : 3
};
let urlParameters = Object.entries(data).map(e => e.join('=')).join('&');
console.log(urlParameters);
I made an implementation that support nested objects and arrays i.e.
var data = {
users: [
{
"name": "jeff",
"tasks": [
"Do one thing",
"Do second thing"
]
},
{
"name": "rick",
"tasks": [
"Never gonna give you up",
"Never gonna let you down"
]
}
]
}
Will be:
users[0][name]=jeff&users[0][tasks][0]=Do%20one%20thing&users[0][tasks][1]=Do%20second%20thing&users[1][name]=rick&users[1][tasks][0]=Never%20gonna%20give%20you%20up&users[1][tasks][1]=Never%20gonna%20let%20you%20down
So, here's the implementation:
var isObj = function(a) {
if ((!!a) && (a.constructor === Object)) {
return true;
}
return false;
};
var _st = function(z, g) {
return "" + (g != "" ? "[" : "") + z + (g != "" ? "]" : "");
};
var fromObject = function(params, skipobjects, prefix) {
if (skipobjects === void 0) {
skipobjects = false;
}
if (prefix === void 0) {
prefix = "";
}
var result = "";
if (typeof(params) != "object") {
return prefix + "=" + encodeURIComponent(params) + "&";
}
for (var param in params) {
var c = "" + prefix + _st(param, prefix);
if (isObj(params[param]) && !skipobjects) {
result += fromObject(params[param], false, "" + c);
} else if (Array.isArray(params[param]) && !skipobjects) {
params[param].forEach(function(item, ind) {
result += fromObject(item, false, c + "[" + ind + "]");
});
} else {
result += c + "=" + encodeURIComponent(params[param]) + "&";
}
}
return result;
};
var data = {
users: [{
"name": "jeff",
"tasks": [
"Do one thing",
"Do second thing"
]
},
{
"name": "rick",
"tasks": [
"Never gonna give you up",
"Never gonna let you down"
]
}
]
}
document.write(fromObject(data));
You don't need to serialize this object literal.
Better approach is something like:
function getAsUriParameters(data) {
var url = '';
for (var prop in data) {
url += encodeURIComponent(prop) + '=' +
encodeURIComponent(data[prop]) + '&';
}
return url.substring(0, url.length - 1)
}
getAsUriParameters(data); //"action=actualiza_resultado&postID=1&gl=2&gl2=3"
Something I find nicely looking in ES6:
function urlfy(obj) {
return Object
.keys(obj)
.map(k => `${encodeURIComponent(k)}=${encodeURIComponent(obj[k])}`)
.join('&');
}
Later update (same thing, maybe a bit cleaner):
const urlfy = obj => Object
.keys(obj)
.map(k => encodeURIComponent(k) + '=' + encodeURIComponent(obj[k]))
.join('&');
Like #georg said, you can use JQuery.param for flat objects.
If you need to process complex objects, you can use JsonUri, a python package that does just that. There is JavaScript library for it as well
Disclaimer: I am the author of JSONURI
Edit: I learned much later that you can also just base64 encode your payload - most languages as support for base64 encoding/decoding
Example
x = {name: 'Petter', age: 47, places: ['Mozambique', 'Zimbabwe']}
stringRep = JSON.stringify(x)
encoded = window.btoa(stringRep)
Gives you eyJuYW1lIjoiUGV0dGVyIiwiYWdlIjo0NywicGxhY2VzIjpbIk1vemFtYmlxdWUiLCJaaW1iYWJ3ZSJdfQ==, which you can use as a uri parameter
decoded = window.atob(encoded)
originalX = JSON.parse(decoded)
Needless to say, it comes with its own caveats
But i wonder if there any function in javascript
Nothing prewritten in the core.
or json to do this?
JSON is a data format. It doesn't have functions at all.
This is a relatively trivial problem to solve though, at least for flat data structures.
Don't encode the objects as JSON, then:
function obj_to_query(obj) {
var parts = [];
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
parts.push(encodeURIComponent(key) + '=' + encodeURIComponent(obj[key]));
}
}
return "?" + parts.join('&');
}
alert(obj_to_query({
'action': 'actualiza_resultado',
'postID': 1,
'gl': 2,
'gl2': 3
}));
There isn't a standard way to encode complex data structures (e.g. with nested objects or arrays). It wouldn't be difficult to extend this to emulate the PHP method (of having square brackets in field names) or similar though.
This one processes arrays with by changing the nameinto mutiple name[]
function getAsUriParameters (data) {
return Object.keys(data).map(function (k) {
if (_.isArray(data[k])) {
var keyE = encodeURIComponent(k + '[]');
return data[k].map(function (subData) {
return keyE + '=' + encodeURIComponent(subData);
}).join('&');
} else {
return encodeURIComponent(k) + '=' + encodeURIComponent(data[k]);
}
}).join('&');
};
Best solution for Vanilla JavaScript:
var params = Object.keys(data)
.filter(function (key) {
return data[key] ? true : false
})
.map(function (key) {
return encodeURIComponent(key) + '=' + encodeURIComponent(data[key])
})
.join('&');
PS: The filter is used here to remove null or undefined parameters. It makes the url look cleaner.
The custom code above only handles flat data. And JQuery is not available in react native. So here is a js solution that does work with multi-level objects and arrays in react native.
function formurlencoded(data) {
const opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
let sorted = Boolean(opts.sorted),
skipIndex = Boolean(opts.skipIndex),
ignorenull = Boolean(opts.ignorenull),
encode = function encode(value) {
return String(value).replace(/(?:[\0-\x1F"-&\+-\}\x7F-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])/g, encodeURIComponent).replace(/ /g, '+').replace(/[!'()~\*]/g, function (ch) {
return '%' + ch.charCodeAt().toString(16).slice(-2).toUpperCase();
});
},
keys = function keys(obj) {
const keyarr = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : Object.keys(obj);
return sorted ? keyarr.sort() : keyarr;
},
filterjoin = function filterjoin(arr) {
return arr.filter(function (e) {
return e;
}).join('&');
},
objnest = function objnest(name, obj) {
return filterjoin(keys(obj).map(function (key) {
return nest(name + '[' + key + ']', obj[key]);
}));
},
arrnest = function arrnest(name, arr) {
return arr.length ? filterjoin(arr.map(function (elem, index) {
return skipIndex ? nest(name + '[]', elem) : nest(name + '[' + index + ']', elem);
})) : encode(name + '[]');
},
nest = function nest(name, value) {
const type = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : typeof value === 'undefined' ? 'undefined' : typeof(value);
let f = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;
if (value === f) f = ignorenull ? f : encode(name) + '=' + f; else if (/string|number|boolean/.test(type)) f = encode(name) + '=' + encode(value); else if (Array.isArray(value)) f = arrnest(name, value); else if (type === 'object') f = objnest(name, value);
return f;
};
return data && filterjoin(keys(data).map(function (key) {
return nest(key, data[key]);
}));
}
The conversion from a JSON string to a URL query string can be done in a single line:
const json = '{"action":"actualiza_resultado","postID":1,"gl":2,"gl2":3}';
const queryString = new URLSearchParams(JSON.parse(json)).toString();
queryString would then be set to "action=actualiza_resultado&postID=1&gl=2&gl2=3".
Based on georg's answer, but also adding ? before the string and using ES6:
const query = !params ? '': Object.keys(params).map((k, idx) => {
let prefix = '';
if (idx === 0) {
prefix = '?';
}
return prefix + encodeURIComponent(k) + '=' + encodeURIComponent(params[k]);
}).join('&');
As most of the answers only convert flat objects to query parameters, I would like to share mine.
This function can handle flat objects, as well as nested arrays/objects while only using plain JS.
function incapsulateInBrackets(key)
{
return '[' + key + ']';
}
function encode(object, isSubEncode=false, prefix = '')
{
let parts = Object.keys(object).map( (key) => {
let encodedParts = [];
if(Array.isArray(object[key]))
{
object[key].map(function(innerKey, index){
encodedParts.push( encode(object[key][index], true, prefix + key + incapsulateInBrackets(index)));
});
}
else if(object[key] instanceof Object)
{
Object.keys(object[key]).map( (innerKey) => {
if(Array.isArray(object[key][innerKey]))
{
encodedParts.push( encode(object[key][index], true, prefix + incapsulateInBrackets(key) + incapsulateInBrackets(innerKey)) );
}
else
{
encodedParts.push( prefix + incapsulateInBrackets(key) + incapsulateInBrackets(innerKey) + '=' + object[key][innerKey] );
}
});
}
else
{
if(isSubEncode)
{
encodedParts.push( prefix + incapsulateInBrackets(key) + '=' + object[key] );
}
else
{
encodedParts.push( key + '=' + object[key] );
}
}
return encodedParts.join('&');
});
return parts.join('&');
}
Make a utility if you have nodejs
const querystring = require('querystring')
export function makeQueryString(params): string {
return querystring.stringify(params)
}
import example
import { makeQueryString } from '~/utils'
example of use
makeQueryString({
...query,
page
})
Read the latest documentation here.