Replace a + with a space in a string - json

I have a Typescript project where I am converting URL parameters to a JSON object.
The problem is that a value is concatenated with a '+', how can I replace this symbol with a space?
This is the URL:
let paramUrl = 'type=MERCEDES+BENZ'
This is what I do:
let replace = JSON.parse('{"' + paramUrl.replace(/&/g, '","').replace(/=/g,'":"') + '"}', function(key, value) { return key===""?value:decodeURIComponent(value) })
This is what it returns:
{type: "MERCEDES+BENZ"}
This is what I need:
{type: "MERCEDES BENZ"}

Unless you're very skilled with regular expressions I would strongly discourage using them for situations like this. In fact, any kind of explicit manipulation of characters here is going to be harder to debug and maintain than just using available JavaScript APIs to convert your url search params string into a plain object.
Here's how I'd suggest you proceed, assuming your JS runtime has both the URLSearchParams constructor and the Object.fromEntries() method:
let paramUrl = 'type=MERCEDES+BENZ'
const replace = Object.fromEntries(new URLSearchParams(paramUrl));
console.log(replace) // { "type": "MERCEDES BENZ" }
That's essentially a one-liner that first converts your paramUrl string into an object representing a list of string key-value pairs, and then converts these key-value pairs into a plain object. You're not responsible for maintaining or implementing URLSearchParams or Object.fromEntries(); you just have to use or polyfill them (e.g., this polyfill for URLSearchParams).
Be aware that if your search param string has multiple values for the same key (e.g., foo=bar&foo=baz) that you'll end up with just the latter one, but you can deal with that if you need to by switching from Object.fromEntries() to some more custom iteration.
Playground link to code

Whether your conversion makes sense or not, you may convert your result with this:
const replace = {type: "MERCEDES+BENZ"}
const replacedReplace = JSON.parse(JSON.stringify(replace).replace('+', ' '))
console.log(replacedReplace)
Or manipulate the value directly:
let replace = {type: "MERCEDES+BENZ"}
replace.type=replace.type.replace('+',' ')
console.log(replace)

try this
let paramUrl = 'type=MERCEDES+BENZ';
let arr=paramUrl.replaceAll("+"," ").split("=");
let obj={[arr[0]]:arr[1]}; // {type: "MERCEDES BENZ"}
or in one line
let obj= paramUrl.replaceAll("+"," ").split("=")
.reduce( (key,value) => { return { [key] : value}; } ); // {type: "MERCEDES BENZ"}

Related

How do I generate a serde_json object from a "." separated text format?

The Problem
I am trying to generate a json object (with serde) by parsing a custom macro format that looks like this:
Plot.Polar.max: 20
Plot.Polar.min: 0
Plot.Polar.numberlabel: 0101
Plot.Polar.chartname: small-chart
Plot.Polar.Var.1:
Plot.Polar.Var.2: A label: with T+ES[T] #Data
What I get stuck on is how to set the keys for the object. In my old JavaScript code I split on \n, ., and :, had a couple of nested loops, and a reduceRight in the end to create the object like this:
// rowObject equals one row in the old macro format
let rowObject = keys.reduceRight(
(allKeys, item) => ({ [item]: allKeys }),
val,
);
My Goal
My goal is to use that json object to generate a highcharts config (json) depending on the keys and values from the custom macro. I want to be able to print just the macro in json format as well hence why I want to convert the macro to json first and not use a separate data structure (though that might be a good idea?). The json I want to produce from the macro is this:
{
"Plot": {
"Polar": {
"max": 20,
"min": 0
}
}
}
What I Have Tried
Map::insert though I am not sure how to structure the key string. How do I manage the Map objects in this case?
Another solution I see is creating the object from a raw string and merging each rowObject with the main object though this approach feels a bit hacky.
The current loop I have:
// pseudo
// let mut json_macro = new Map();
for row in macro_rows.iter() {
let row_key_value: Vec<&str> = row.split(':').collect();
let keys = row_key_value[0];
let value = row_key_value[1];
let keys_split: Vec<&str> = keys.split('.').collect();
for key in keys_split.iter() {
// TODO: accumulate a objects to row_object
}
// TODO: insert row_object to json_macro
}
The Question
Is it possible to do something like reduceRight in JavaScript or something similar in rust?
Update
I realized that I will have to treat all values as strings because it is impossible to know if a number is a string or not. What worked in the end was the solution #gizmo provided.
To insert your row into json_macro you can fold keys_split from the left and insert every key into the top-level object:
let row_key_value: Vec<&str> = row.split(':').collect();
let keys = row_key_value[0];
let value: Value = serde_json::from_str(row_key_value[1]).unwrap();
let keys_split: Vec<&str> = keys.split('.').collect();
keys_split[..keys_split.len() - 1]
.iter()
.fold(&mut json_macro, |object, &key| {
object
.entry(key)
.or_insert(Map::new().into())
.as_object_mut()
.unwrap()
})
.insert(keys_split.last().unwrap().to_string(), value);
A couple things to note here about unwrap()s:
from_str(...).unwrap(): I parse val as a JSON object here. This might not be what you want. Maybe instead you want str::parse::<i32> or something else. In any case, this parsing might fail.
.as_object_mut().unwrap(): This will explode if the input redefines a key like
Plot.Polar: 0
Plot.Polar.max: 20
The other way around, you probably want to handle the case where the key is already defined as an object.
keys_split.last().unwrap() won't fail but you might want to check if it's the empty string

JSON.parse with changing variable types, how to use double quotes?

I'm trying to make one script to format all possible entities in express. The different entities have different attribute types though. E.g. one entity type has number values for m[attr2], others have string values. How do I get the double quotes only when the attribute type is a string? If I omit the double quotes, string attributes will complain, if I keep them number attributes are saved as strings in the JSON.
I can use if's or a switch and test if attr1 and attr2 are both numbers, then each of them separately and finally the default that both are strings but that seems rather farfetched?
const addSelf = require('./Hal');
const url = require('url');
const formatEntity = (req, m, path, attr1, attr2) => {
let href = url.format({
protocol: req.protocol,
host: req.get('host'),
pathname: `/api/${path}/` + m.id
});
addSelf(m, href);
return JSON.parse(`{
"id": ${m.id},
"${attr1}": "${m[attr1]}",
"${attr2}": "${m[attr2]}",
"_links": ${JSON.stringify(m._links)}
}`);
}
module.exports = formatEntity;
I didn't know much about JSON yet and didn't know I could build objects directly in JS, this is the better option and this way I avoided the mistakes.

Typescript JSON How do I read JSON using hierarchical key like "appInsights:instrumentationKey"

Given the below JSON, I should be able to read the JSON using hierarchical key path such as "appInsights:instrumentationKey". How can i do this in an angular project?
{
"appInsights": {
"instrumentationKey": "<dev-guid-here>"
}
},
Is this what you are looking for ?
const obj = JSON.parse('your json');
const val1 = obj.appInsights.instrumentationKey;
const val2 = obj['appInsights'].instrumentationKey;
UPDATE: Will has fair point. If your JSON data doesnt contain user input e.g won't contain anything harmful that will execute in browser then
const val = eval('obj.' + keyPath.replace(/:/g, '.'));
or if you use Lodash: get
Something along these lines perhaps:
getJsonProperty(jsonObj, keyPath: string) {
const keys: string[] = keyPath.split(':');
return jsonObj[keys[0]][keys[1]];
}
If you don't know how many levels the object can have, you could write a switch statement with cases for each accepted length of the keys Array. Obviously that means hard-coding each case (and a fixed maximum number of possible levels), unless there's some other way of using string literals I'm forgetting.

Create JSON from a String

I am trying to create a json from this String
var json = { data: [v1,v2], con: [begin: "test1", end: "test2"] };
What is wrong with the String? I get an error SyntaxError: Unexpected token :It is not possible to set a key for the value test1 and test2?
In JavaScript:
An object literal, which uses the {} syntax, consists of a collection of property: value pairs.
An array literal, which uses the [] syntax, consists of a collection of values.
[begin: "test1", end: "test2"]
You have used the array syntax here, but have tried to put property: value pairs inside it.
This causes your error, the : isn't expected.
You probably want to use the {} there. Alternatively, you want to remove begin: and end:.
This has nothing to do with JSON or strings. It is simply JavaScript.
You are instantiating a javascript Object.
const toto = {};
is the same as
const toto = new Object();
This is a String javascript object, which hold a string representation of a json.
const toto = "{ \"key\": \"value\" }";
Try
var json = { data: ["v1","v2"], con: [{begin: "test1"}, {end: "test2"}] };
It looks like you might have a blocking error.

d3.js forced layout graph: parse String to link-array

I am referring to this example: http://bl.ocks.org/mbostock/1153292 ,
specifically to these beautiful lines of code:
var links = [
{source: "Microsoft", target: "Amazon", type: "licensing"},
...
{source: "Nokia", target: "Qualcomm", type: "suit"}
];
var nodes = {};
// Compute the distinct nodes from the links.
links.forEach(function(link) {
link.source = nodes[link.source] || (nodes[link.source] = {name: link.source});
link.target = nodes[link.target] || (nodes[link.target] = {name: link.target});
});
I'd like to know what data format 'var links' here has, and how I would parse the following java string (with essentially the same content)
String someLinks = "[{source: \"Microsoft\", target: ... }, ...]"
into something equal to the javascript 'var links' above, from which I can then create the graph.
I've tried
var links = JSON.parse( string );
But that doesn't do the job... 'var links' doesn't seem to be JSON?
EDIT: Or should I use a different java format, e.g. some kind of Array? (Doesn't have to be a String)
Your value in String someLinks is no valid JSON. It lacks the quotes on the attribute names. JSON is more pedantic than javascript itself.
JSON.parse('[{"source":"A"},{"source":"B"}]')
will work (note the " around source.
Also you should avoid writing "protocols" by hand. If you need to build a JSON string in javascript, you can use
JSON.stringify([{source:'A'},{source:'B'}])
Note that JSON e.g. also escapes / for security reasons. An oversight like this can quickly end in errors or worse.
I believe this is JSON in a string. Since JSON is simply JavaScript, you might be able to simply "run" the code to create the corresponding data structure. So try
var links = eval(someLinks)
Even though I have the suspicion if we take a step back, things might be handled differently and you wouldn't need eval in the first place (since it's generally considered bad style)