This question already has answers here:
Encode URL in JavaScript
(22 answers)
Closed 3 years ago.
How to append comma seperated values to url as search params using history.pushsate. with use of , RFC 3986, specifies that URI path components should not contain unencoded reserved characters and comma is one of those reserved characters. https://www.rfc-editor.org/rfc/rfc3986.
#code
window.history.pushState('new', 'inventory', '/new');
#Desired result
https://www.test.com/new?Year=2020,2019&Pricerange=10001-20000,20001-30000,30001-40000&Mileagerange=1001-2000,2001-3000&Bodystyle=4dr%20Car,Convertible
#Data i wanted to append
{
"year": [
"2017",
"2018"
],
"model": [
"Escape",
"Edge"
],
"mileage": [
"1-1000"
],
"bodyStyle": [
"Convertible",
"4dr Car",
"2dr Car"
],
"priceRange": [
"$20,000-$30,000",
"$30,000-$40,000"
]
}
jQuery.param() seems to repeat array values, so you'll have to find some existing library or write custom function.
Something like that (but maybe optimized a bit more):
/**
* Prepare `search` part of URL.
* #param {object} o Each key is a param name and value is an array of values
* #return {string}
*/
function createURLQuery (o) {
return Object.keys(o)
.map(key => `${encodeURIComponent(key)}=`
+ o[key].map(v => encodeURIComponent(v)).join(','))
.join('&')
}
// Example code
const data = {
"year": [
"2017",
"2018"
],
"model": [
"Escape",
"Edge"
],
"mileage": [
"1-1000"
],
"bodyStyle": [
"Convertible",
"4dr Car",
"2dr Car"
],
"priceRange": [
"$20,000-$30,000",
"$30,000-$40,000"
]
};
const newURL = `/new?${createURLQuery(data)}`;
console.log('Pushing new URL', newURL);
// window.history.pushState('new', 'inventory', newURL);
You'll have to implement any additional changes to data yourself. "Desired result" seems to drop "$" characters and capitalize parameter names.
Parse the JSON data to a JS object and parse the keys and values according to your needs. I don't think there's any way to achieve exactly the string format that you want, unless someone wrote a npm package for exactly this case (not deeply nested json object to a query string).
Make use of JSON.parse() and Object.entries() to build a query string based on the JSON input.
I've made an example to have a look at. You still need some parsing for currency and maybe something more.
See below if REPL link wouldn't work.
const json = `
{
"year": [
"2017",
"2018"
],
"model": [
"Escape",
"Edge"
],
"mileage": [
"1-1000"
],
"bodyStyle": [
"Convertible",
"4dr Car",
"2dr Car"
],
"priceRange": [
"$20,000-$30,000",
"$30,000-$40,000"
]
}`;
const parsedJson = JSON.parse(json);
let queryString = '?';
Object.entries(parsedJson).forEach(([key, value], index) => {
let string = `${index != 0 ? '&' : ''}${key}=${value.join()}`;
queryString += string;
});
const encodedQueryString = encodeURIComponent(queryString.trim())
Related
This question already has answers here:
JSON.stringify: how to skip indentation for one (or more) objects
(1 answer)
Json - stringify so that arrays are on one line
(10 answers)
JSON.stringify custom formatting
(3 answers)
Closed 3 months ago.
I am working on a JSON file generator while everything works fine the final result is not as good as expected
With following stringify function call, following json file would be generated.
var jsonString = JSON.stringify(object, null, 2);
{
"key_1": 2397,
"key_2": "text1",
"key_3": "text",
"key_4": [
"12345",
"23456"
],
"key_5": [
0,
1
],
"key_6": [
0
],
"key_7": [
"https://xxxx",
"https://xxxx"
],
"key_8": "12345",
}
In the JSON file I want to make key_5 and key_6 skip the stringify attributes and make it like this
"key_5": [0,1],
"key_6": [0],
So the final file would look like this
{
"key_1": 2397,
"key_2": "text1",
"key_3": "text",
"key_4": [
"12345",
"23456"
],
"key_5": [0,1],
"key_6": [0],
"key_7": [
"https://xxxx",
"https://xxxx"
],
"key_8": "12345",
}
I think it is possible to customized the rule for certain key but I am very new to JS so I am not sure how to do so...
jsonString = JSON.stringify(object, (key, value) => {
if (key === "key_5"||key === "key_6") return Do_something_ELSE;
return value;
}, 2);
I have the following array with multiple arrays inside the object, how do I generate a query string, where the output should be.
Where services and accountTypes for example that are entries from array it becomes a key in query string.
The value in query string is the id from object in each array
Output should be for example
services=10&services=30&accountTypes=20
Array
[
{
"services": [
{
"id": "10",
"name": "PIX"
},
{
"id": "30",
"name": "Income"
},
],
"accountTypes": [
{
"id": "20",
"name": "Digital Account"
}
]
}
]
My function that I tried.
I tried it with the encodeURIComponent as below, but it's generating undefined
const params = initialFilterDataJson.map((param: QueryParamsType) => {
return encodeURIComponent(param.key) + '=' + encodeURIComponent(param.id)
})
const queryString = params.join('&')
http://localhost:3000/api/accounts?undefined=undefined
Your code is not nesting deep enough in the data structure. You need to have nested loops, first to visit the objects in the outer array, then to visit the keys (with corresponding arrays) of such an object, and then to visit each inner object in such an array:
const initialFilterDataJson = [{"services": [{"id": "10","name": "PIX"},{"id": "30","name": "Income"},],"accountTypes": [{"id": "20","name": "Digital Account"}]}];
const queryString = initialFilterDataJson.flatMap((param) =>
Object.entries(param).flatMap(([key, arr]) =>
arr.map(({id}) => encodeURIComponent(key) + '=' + encodeURIComponent(id))
)
).join('&');
console.log(queryString);
Side note: some web servers can deal better with duplicate keys in the query string when they are suffixed with [], like services[]=10&services[]=20.
I want to fetch all the names and label from JSON without loop. Is there a way to fetch with any filter method?
"sections": [
{
"id": "62ee1779",
"name": "Drinks",
"items": [
{
"id": "1902b625",
"name": "Cold Brew",
"optionSets": [
{
"id": "45f2a845-c83b-49c2-90ae-a227dfb7c513",
"label": "Choose a size",
},
{
"id": "af171c34-4ca8-4374-82bf-a418396e375c",
"label": "Additional Toppings",
},
],
},
]
}
When you say "without loops" I take it as without For Loops. because any kind of traversal of arrays, let alone nested traversal, involve iterating.
You can use the reduce method to have it done for you internally and give you the format you need.
Try this :
const data = {
sections: [
{
id: "62ee1779",
name: "Drinks",
items: [
{
id: "1902b625",
name: "Cold Brew",
optionSets: [
{
id: "45f2a845-c83b-49c2-90ae-a227dfb7c513",
label: "Choose a size"
},
{
id: "af171c34-4ca8-4374-82bf-a418396e375c",
label: "Additional Toppings"
}
]
}
]
}
]
};
x = data.sections.reduce((acc, ele) => {
acc.push(ele.name);
otherName = ele.items.reduce((acc2, elem2) => {
acc2.push(elem2.name);
label = elem2.optionSets.reduce((acc3, elem3) => {
acc3.push(elem3.label);
return acc3;
}, []);
return acc2.concat(label);
}, []);
return acc.concat(otherName);
}, []);
console.log(x);
Go ahead and press run snippet to see if this matches your desired output.
For More on info reduce method
In the context of cJSON
yes, we can fetch the key value for any of the object.
1 - each key value is pointed by one of the objects. will simply fetch that object and from there will get the key value.
In the above case for
pre-requisition: root must contain the json format and root must be the cJSON pointer. if not we can define it and use cJSON_Parse() to parse the json.
1st name object is "sections" will use
cJSON *test = cJSON_GetObjectItem(root, "sections");
char *name1 = cJSON_GetObjectItem(test, "name" )->valuestring;
2nd name key value
cJSON *test2 = cJSON_GetObjectItem(test, "items");
char *name2 = cJSON_GetObjectItem(tes2, "name")->valuestring;
likewise, we can do for others as well to fetch the key value.
I am trying to extract "animal" and "fish" hashtags from the JSON object below. I know how to extract the first instance named "animal", but I have no idea how to extract both instances. I was thinking to use a loop, but unsure where to start with it. Please advise.
data = '{"hashtags":[{"text":"animal","indices":[5110,1521]},
{"text":"Fish","indices":[122,142]}],"symbols":[],"user_mentions":
[{"screen_name":"test241","name":"Test
Dude","id":4999095,"id_str":"489996095","indices":[30,1111]},
{"screen_name":"test","name":"test","id":11999991,
"id_str":"1999990", "indices":[11,11]}],"urls":[]}';
function showHashtag(data){
i = 0;
obj = JSON.parse(data);
console.log(obj.hashtags[i].text);
}
showHashtag(data);
Use Array.prototype.filter():
let data = '{"hashtags":[{"text":"animal","indices":[5110,1521]},{"text":"Fish","indices":[122,142]}],"symbols":[],"user_mentions":[{"screen_name":"test241","name":"Test Dude","id":4999095,"id_str":"489996095","indices":[30,1111]}, {"screen_name":"test","name":"test","id":11999991, "id_str":"1999990", "indices":[11,11]}],"urls":[]}';
function showHashtag(data){
return JSON.parse(data).hashtags.filter(e => /animal|fish/i.test(e.text))
}
console.log(showHashtag(data));
To make the function reusable, in case you want to find other "hashtags", you could pass an array like so:
function showHashtag(data, tags){
let r = new RegExp(tags.join("|"), "i");
return JSON.parse(data).hashtags.filter(e => r.test(e.text))
}
console.log(showHashtag(data, ['animal', 'fish']));
To get only the text property, just chain map()
console.log(showHashtag(data, ['animal', 'fish']).map(e => e.text));
or in the function
return JSON.parse(data).hashtags
.filter(e => /animal|fish/i.test(e.text))
.map(e => e.text);
EDIT:
I don't really get why you would filter by animal and fish if all you want is an array with ['animal', 'fish']. To only get the objects that have a text property, again, use filter, but like this
let data = '{"hashtags":[{"text":"animal","indices":[5110,1521]},{"text":"Fish","indices":[122,142]}],"symbols":[],"user_mentions":[{"screen_name":"test241","name":"Test Dude","id":4999095,"id_str":"489996095","indices":[30,1111]}, {"screen_name":"test","name":"test","id":11999991, "id_str":"1999990", "indices":[11,11]}],"urls":[]}';
function showHashtag(data){
return JSON.parse(data).hashtags
.filter(e => e.text)
.map(e => e.text);
}
console.log(showHashtag(data));
For me, Lodash can be of great use here, which have different functions in terms of collections. For your case i'd use _.find function to help check the array and get any of the tags with the creteria passed in as second argument like so:
.find(collection, [predicate=.identity], [fromIndex=0])
source npm package
Iterates over elements of collection, returning the first element
predicate returns truthy for. The predicate is invoked with three
arguments: (value, index|key, collection).
with your case this should work
var data = '{ "hashtags": [ { "text": "animal", "indices": [ 5110, 1521 ] }, { "text": "Fish", "indices": [ 122, 142 ] } ], "symbols": [], "user_mentions": [ { "screen_name": "test241", "name": "Test \n Dude", "id": 4999095, "id_str": "489996095", "indices": [ 30, 1111 ] }, { "screen_name": "test", "name": "test", "id": 11999991, "id_str": "1999990", "indices": [ 11, 11 ] } ], "urls": [] }';
var obj = JSON.parse(data);
_.find(obj.hashtags, { 'text': 'animal' });
// => { "text": "animal", "indices": [ 5110, 1521 ] }
For simple parsing like this one, I would use the plain old obj.forEach() method, it is more readable and easy to understand, especially for javascript beginner.
obj = JSON.parse(data).hashtags;
obj.forEach(function(element) {
console.log(element['text']);
});
I'm creating an index file in JSON, which I'm using as a sort-of-database index for a javascript application I'm working on.
My index will look like this:
{
"_id": "acomplex_indices.json",
"indexAB": {
"title": {
"Shawshank Redemption": [
"0"
],
"Godfather": [
"1"
],
"Godfather 2": [
"2"
],
"Pulp Fiction": [
"3"
],
"The Good, The Bad and The Ugly": [
"4"
],
"12 Angry Men": [
"5"
],
"The Dark Knight": [
"6"
],
"Schindlers List": [
"7"
],
"Lord of the Rings - Return of the King": [
"8"
],
"Fight Club": [
"9"
],
"Star Wars Episode V": [
"10"
],
"Lord Of the Rings - Fellowship of the Ring": [
"11"
],
"One flew over the Cuckoo's Nest": [
"12"
],
"Inception": [
"13"
],
"Godfellas": [
"14"
]
},
"year": {
"1994": [
"0",
"3"
],
"1972": [
"1"
],
"1974": [
"2"
],
"1966": [
"4"
],
"1957": [
"5"
],
"2008": [
"6"
],
"1993": [
"7"
],
"2003": [
"8"
],
"1999": [
"9"
],
"1980": [
"10"
],
"2001": [
"11"
],
"1975": [
"12"
],
"2010": [
"13"
],
"1990": [
"14"
]
}
}
}
So for every keyword (like Pulp Fiction), I'm storing the matching document-id(s).
My problem is with integers/numbers/non-string data, like the release year in the above example. This is stored as a string, while I had hoped it would be stored as a number.
I'm creating the index entries like this:
// indices = the current index file
// doc = the document to update the index with
// priv.indices = all indices defined for this application instance
// priv.indices.fields = index fields e.g. "year", "director", "title"
// priv.indices.name = name of this index
priv.updateIndices = function (indices, doc) {
var i, j, index, value, label, key, l = priv.indices.length;
// loop all indices to add document
for (i = 0; i < l; i += 1) {
index = {};
index.reference = priv.indices[i];
index.reference_size = index.reference.fields.length;
index.current = indices[index.reference.name];
for (j = 0; j < index.reference_size; j += 1) {
label = index.reference.fields[j]; // like "year"
value = doc[label]; // like 1985
// if document has a label field (e.g. doc.year = 1985)
if (value !== undefined) {
// check if the index file already contains an entry for 1985
index.current_size = priv.getObjectSize(index.current[label]);
if (index.current_size > 0) {
// check if the document id is already in the index
// in case the data is updated (e.g. change 1982 to 1985)
key = priv.searchIndexByValue(
index.current[label],
doc._id,
"key"
);
if (!!key) {
delete index.current[label][key];
}
}
// create a new array if 1985 is not in the index yet
if (index.current[label][value] === undefined) {
index.current[label][value] = [];
}
// add the document id to an existing entry
index.current[label][value].push(doc._id);
}
}
}
return indices;
};
This works fine, except that fields I want to store as non-strings (integers, numbers or datetime), like the year in the above example end up as strings in my index.
Question:
Is it at all possible to store "non-string" types in a JSON document? If so, can I also store the key of a key/value pair as a "non-string" element.
If not, would I have to add a parameter to my index definitions declaring the type of each key in order to modify the key-string when I run into it or is there a better way to do it?
Thanks!
Is it at all possible to store "non-string" types in a JSON document?
Yes. The value of a property can be a string, number, boolean, object, array or null (undefined is a notable exception - it's a native JavaScript type but it's not a valid JSON value).
Can I also store the key of a key/value pair as a "non-string" element?
No. The key name must always be a string. However, that doesn't mean you can't parse that string into some other JavaScript type. For example, if you have a string but need a number, you can use the parseInt function, or the unary + operator.
See the JSON grammar for more detail.
no you can't, in JSON keys are strings.
the best you can do is storing string representations of those keys, wether integer or objects(more complicated, you have to build a serialization function).
If you want to use only consecutive integers keys starting from 0, then you can use arrays.
According to the json spec, you can have a number anywhere you could have a value. So the key of an object must be a string, but the value can be a number. Also any of the values in an array can be a number.
The spec is beside the point though; I believe the issue is this line:
index.current[label][value].push(doc._id);
When you read doc._id, that is a string. If you want to store it in the JSON as a number, you need to cast it:
index.current[label][value].push(parseInt(doc._id, 10));
Also note that having just numbers as IDs is not valid HTML.