How to Read Custom Format that Looks Like JSON - javascript

When importing data from a file this is one of the results:
{
id: 1234,
name: "Store name",
street: "Street",
house_number: "19",
postal_code: "12346",
city: "Brussel",
country: "NL",
formatted_opening_times: {2=>[09:00 - 22:00] 4=>[09:00 - 22:00] 6=>[10:00 - 18:00] 1=>[09:00 - 22:00] 5=>[09:00 - 20:00] 3=>[09:00 - 22:00] 0=>[09:00 - 22:00]},
open_now: true
}
We are able to show the data, but have difficulty with the formatted_opening_times. We would like to show it like:
Monday: 09:00 - 02:00
Tuesday: 09:00 - 22:00
...
I have tried to parse the data in JSON and show the content:
var opening_times = JSON.parse(storeSpecs.data.formatted_opening_times);
var content = "Monday: "
content += ${opening_times[0][2]}
But result in error console is saying
Uncaught SyntaxError: Unexpected number in JSON at position 1
Hope someone can help.

That is not JSON. A hack would be to replace the "=>" with ":" and the "[" or "]" with quotes.
var org = "{2=>[09:00 - 22:00] 4=>[09:00 - 22:00] 6=>[10:00 - 18:00] 1=>[09:00 - 22:00] 5=>[09:00 - 20:00] 3=>[09:00 - 22:00] 0=>[09:00 - 22:00]}"
var jsonStr = org.replace(/([0-9]+)=>/g, '"$1":')
.replace(/\[/g,'"')
.replace(/\]/g, '",')
.replace(/,\}$/, '}')
var res = JSON.parse(jsonStr);

Related

How to replace list of words with <span> tag at multiple indexes in Javascript

I have a response of parent string which I have to modify and replace with the provided start and end indexes.
let parentString = '\r\nManchester United won\r\nManchester City lost\r\nLeeds United tied'
let replaceValues =
{value: 'Manchester United', startIndex: 2, endIndex: 19}
{value: 'Manchester City', startIndex: 25, endIndex: 40}
{value: 'Leeds United', startIndex: 47, endIndex: 59}
Expected Final Result:
I tried below approach but was not successful
replaceAt(input: string, index: number, replacement: string, source: string) {
return (
input.substring(0, index) +
replacement +
input.substring(index + source.length)
);
}
Usage:
replaceValues.forEach((replaceMatch: any) => {
parentString = this.replaceAt(
parentString,
replaceMatch.startIndex,
"<span class='replace-text-{{i}}'>${replaceMatch.value}</span>",
replaceMatch.value
);
please ignore my example names couldn't think anything more
EDIT: My previous answer did not account to duplicate and did not use your indexes, so here it is a more consistent answer:
Convert string to array to ease manipulation
const parentArray = Array.from(parentString)
Now we have an array of characters, i.e [" ", " ", "M", "a", "n", "c", "h", ...]
For each item in replaceValues we use splice on our newly created array. Splice acctepts 3 arguments:
First argument is the start index where we want to splice the array.
Second argument is how many items in the array will be deleted/replaced.
Third argument is with what we want to replace the array portion.
let numberOfCharsReplaced = 0
replaceValues.forEach(item => {
parentArray.splice(item.startIndex - numberOfCharsReplaced, item.endIndex - item.startIndex, `<span>${item.value}</span>`)
numberOfCharsReplaced = numberOfCharsReplaced + item.endIndex - item.startIndex - 1
console.log(parentArray, numberOfCharsReplaced)
})
That numberOfCharsReplaced is necessary because since we splice and replace, we need to take account of the number of chars that has been replaced, what I am saying is that when we replace the 'Manchester United' word that has 16 chars, we pass from 16 items in the array to only 1 big word (i.e "<span>Manchester United</span>") so we can't rely on the startIndex of the next value only, we need to do some calculation. It's easier in the code.
We get back our string by using .join(), telling to the join method with which character we want to join each character.
const replacedParentString = parentArray.join("");
If you still wish to have an array of html string, use the split and shift method indicated in the old answer
Please refer to MDN to read more about the methods used in this answer
splice
join
OLD ANSWER
Use values to replace names with their 'html' equivalent within the parent string
replaceValues.forEach(item => {
parentString = parentString.replace(item.value, `<span>${item.value}</span>`)
})
Now you have a string that is like this:
\r\n<span>Manchester United</span> won\r\n<span>Manchester City</span> lost\r\n<span>Leeds United</span> tied
So now you may want this string as an array of html content
let contentsArray = parentString.split("\r\n")
Now we have this:
[
"",
"<span>Manchester United</span> won",
"<span>Manchester City</span> lost",
"<span>Leeds United</span> tied"
]
Finally if you want to get rid of that initial empty string just shift the array once
contentsArray.shift()
If you don't want to use regex you can try this code :
let parentString = '\r\nManchester United won\r\nManchester City lost\r\nLeeds United tied'
let replaceValues = [
{value: 'Manchester United', startIndex: 2, endIndex: 19},
{value: 'Manchester City', startIndex: 25, endIndex: 40},
{value: 'Leeds United', startIndex: 47, endIndex: 59},
];
replaceValues.sort((a,b) => b.startIndex - a.startIndex);
function replaceAt(input, start, end, value) {
let str = input.split('')
str.splice(start, end - start, value);
return str.join('');
}
for(let replace of replaceValues) {
parentString = replaceAt(parentString,replace.startIndex, replace.endIndex, `<span class='replace-text-{{i}}'>${replace.value}</span>`);
}
console.log(parentString);
// Output :
// <span class='replace-text-{{i}}'>Manchester United</span> won
// <span class='replace-text-{{i}}'>Manchester City</span> lost
// <span class='replace-text-{{i}}'>Leeds United</span> tied
I don't know where does {{i}} comes from, but I think you can easily fill it will the correct value
Maybe regex is slightly faster? Seems like you indend to get rid of line breaks?
const parentString = '\r\nManchester United won\r\nManchester City lost\r\nLeeds United tied'
const repalcedString = parentString.replace(/(\r\n|\n|\r)/gm, "");
console.log(repalcedString)

How can i convert a text into an array of useful data?

I am extracting text from a server. The data I'm extracting is not organized for further use. The text I'm extracting looks like this:-
>>[Extracted] id: 194805284, got 55 points from jones (252906152669) date: 15/04/19 08:44:40 you have 30 points remaining
I don't want all this text I only want the id, points, number, and date.
Note: I might extract more than one of the message once in a while.
So to extract the id, points, number, and date, I wrapped every word with a span tag and then used this code:
var getData = {
//gets the id, points, date and number respectively
number1 : $('span:contains("id:")').next().text(),
amount : $('span:contains("got")').next().text(),
time : $('span:contains("date:")').next().text(),
number : $('span:contains("date:")').prev().text()
}
The reason I'm using this code is that I might extract automatically more than 1 message, so with every message that gets extracted, every word that it contains is the same except the id, points, date, and number.
I used the above code to extract the data I want, but this time there was 2 [extracted] messages, look below.
HTML
<p>[Extracted] id: 194805284, got 55 points from jones (252906152669)
date: 15/04/19 08:44:40 you have 30 points remanining [Extracted] id: 193537533, got 3 points from Micheal (907794804)
date: 14/04/19 10:15:32, you have 100 points remaining</p>
<div class="processed-data">
</div>
CSS:
span {
border: 1px solid red;
}
JS:
// wrap every word with <span> tag
var words = $("p").text().split(" ");
$("p").empty();
$.each(words, function(i, v) {
$("p").append($("<span>").text(v));
});
//extract the id, points, time and number respectively
var getData = {
number1: $('span:contains("id:")').next().text(),
amount: $('span:contains("got")').next().text(),
//amount : $('span:contains("got")').next().text().substring(1),
time: $('span:contains("date:")').next().text(),
number: $('span:contains("date:")').prev().text()
}
// Output the extracted data to .processed-data div
$('.processed-data').append("thisTime = { [id: " + getData.number1 + " amount: " + getData.amount + ", time: " + getData.time + " number: " + getData.number + "]}'");
Here's a JSFiddle
output:
thisTime = {[id: 194805284,193537533, amount: 553, time: 15/04/1914/04/19 number: (252906152669) (907794804) ]}'
The results I expect are:
For each [extracted] message to get its own array. By using a loop or anything else.
Example:
Now I'm getting this;
thisTime = {
[id: 194805284,193537533, // All the ids are stored in 1 array data
amount: 553, // All the points are stored in 1 array data e.t.c
time: 15/04/1914/04/19
number: (252906152669) (907794804)]
}
I want to get:
thisTime = {
[id: 194805284,
amount: 55,
time: 15/04/19
number: (252906152669)],
[id:193537533,
amount: 3,
time: 14/04/19
number: (907794804)]
}
I only want each message I extract to have its own array.
I suggest you use Regex to solve it, I think is better than Jquery method that you are using.
See a possible Regex solution:
var text = '[Extracted] id: 194805284, got 55 points from jones (252906152669) date: 15/04/19 08:44:40 you have 30 points remanining [Extracted] id: 193537533, got 3 points from Micheal (907794804) date: 14/04/19 10:15:32, you have 100 points remaining';
var textArray = text.split('[Extracted]');
var regularExpression = /id:\s+([0-9]+).+got\s+([0-9]+).+[^\(]+\(([0-9]+)\)\s+date:\s+([0-9\/\s:]+)/i;
var output = [];
var item;
for(var i = 1; i < textArray.length; i++){
item = textArray[i].match(regularExpression);
output.push({
id: item[1].trim(),
amount: item[2].trim(),
time: item[4].trim(),
number: item[3].trim()
});
}
console.log(output);
You could easily use a regular expression (Regex) to solve this -- is there any particular reason you're wrapping each word in a span?
The following regular expression should match all tokens in your string:
id:\s+(\d+),\s+got\s+(\d+)\s+points\s+from\s+.+?\s+\((\d+)\)\s+date:\s+(\d+)\/(\d+)\/(\d+)\s+(\d+):(\d+):(\d+)
I'm using \s+ here instead of spaces because it seems the spacing in your above template is inconsistent, and just to be safe I like to use \s+ for any whitespace of any quantity.
You can extract a message like so...
const regex = /id:\s+(\d+),\s+got\s+(\d+)\s+points\s+from\s+.+?\s+\((\d+)\)\s+date:\s+(\d+)\/(\d+)\/(\d+)\s+(\d+):(\d+):(\d+)/; // construct the regex literal
const message = // some string matching your "extracted" template
const match = message.match(regex); // now your match contains all the data
const [fullMatch, idString, pointString, dayString, monthString, yearString, hourString, minuteString, secondString] = match; // you don't have to destructure, but this is the order of the capturing groups.
You should also be able to match multiple as well, by doing the following...
let match;
while (match = regex.exec(message)) {
// now match can be handled the same way as above. You could alternatively push the matches to a list as well here.
}
Your issue is getData. I suggest to decompose the string splitting on Extracted and after on spaces. After, you can select sub spans grouping by sentences and filter in order to create an array containing one or more objects.
var sentences = $("p").text().split("\[Extracted\]").slice(1);
$("p").empty();
$.each(sentences, function(i, v) {
var words = ['Extracted'].concat(v.trim().split(/ +/));
$.each(words, function(idx, word) {
$("p").append($("<span/>", {text: word.trim()}));
});
});
var result = {thisTime: $("p span:contains(Extracted)").map(function(idx, txt) {
var x = $(this).nextUntil('span:contains(Extracted)');
return {id: x.filter('span:contains("id:")').next().text(),
amount: x.filter('span:contains("got")').next().text(),
time: x.filter('span:contains("date:")').next().text(),
number: x.filter('span:contains("date:")').prev().text()};
}).get()};
$('.processed-data').append(JSON.stringify(result));
span {
border: 1px solid red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<p>[Extracted] id: 194805284, got 55 points from jones (252906152669)
date: 15/04/19 08:44:40 you have 30 points remanining [Extracted] id: 193537533, got 3 points from Micheal (907794804)
date: 14/04/19 10:15:32, you have 100 points remaining</p>
<div class="processed-data">
</div>

restAPI- how to format JSON value

I am getting JSON value from my server and it works well. But I want to customize my JSON value. I have two questions:
First, when I get region value, I get it like ["USA"],["Mexico"],["Canada"]. Is there a way I can ignore the double quotes/brackets and just get the strings values like USA, Mexico, Canada?
Second, when I get the regDate value I get the whole data like 2018-10-31T07:53:12.000Z instead, can I ignore some values and get it like 2018-10-31 07:53?
{
"result": "ok",
"data": [
{
"idx": 1,
"region": "[\"USA \", \"Mexico \", \"Canada \"]",
"regDate": "2018-10-31T07:53:12.000Z"
}
]
}
Seems that your region data was double-encoded. In javascript, you'll want to decode that, e.g.
const response = {
"result": "ok",
"data": [
{
"idx": 1,
"region": "[\"USA \", \"Mexico \", \"Canada \"]",
"regDate": "2018-10-31T07:53:12.000Z"
}
]
};
const regions = JSON.parse(response.data[0].region);
For time formatting, you could use the built-in Javascript Date type, such as..
const regDate = new Date(response.data[0].regDate);
const regDatestr =
regDate.getUTCFullYear() + '-' +
regDate.getUTCMonth() + '-' +
regDate.getDate() + ' ' +
regDate.getUTCHours() + ':' +
regDate.getUTCMinutes()
;
If you need more functionality than the built-in, I would recommend date-fns.
You'd save the results of your JSON query to a variable data. Then you'd do this:
var countries = "";
for (var i = 0; i < data[0].region.length; i++) {
countries += data[0].region[i];
}
var time = data[0].regDate.split("000Z");

Extract information from string - JavaScript

I am currently implementing google places autocomplete and the module I am using in React Native gives me the address as a whole string and not in address components. However, I need to have the postal code and city separate. The example response always look like this:
address: 'Calle Gran Vía, 8, 28013 Madrid, Spain
From this string I would need to have an object that looks like this:
{
city: 'Madrid',
postal_code: 28013,
}
How could I achieve this?
It's not the most "clean" or "smooth" answer, but it's something:
var response = "address: 'Calle Gran Vía, 8, 28013 Madrid, Spain";
var subStr = response.split(",")[2];
var obj = {
city: subStr.split(" ")[2],
postal_code: subStr.split(" ")[1]
};
console.log(obj);
For the city I think the best way is to use an array of cities and search it in the string
var str = "Calle Gran Vía, 8, 28013 Madrid, Spain";
var cities = ["Paris", "Berlin", "Madrid"];
var city = cities.filter(function(item) {
if (str.search(item) != -1)
return item;
})[0] || null;
For the postal code you should use a regex depending on the country (a good list of regex by country)
Probably split the string by ',' with array methods, take the third element of the array and split that by ' ', then you have your data points.
If you can always count on it being in that same format, you can do the following.
var splitAdress = address.split(",");
//This will give you ["Calle Gran Vía", " 8", " 28013 Madrid", " Spain"]
splitAdress = splitAdress[2].split(" ");
//This will give you ["", "28013", "Madrid"]
You'll first split the string into an array based on the comma and then follow it up by splitting on the space. The extra element in the second array is due to the space. This is an example of what #CBroe pointed out in the comments.
list=adress.split(",")[2].split()
list[0] gives you the postal code
list[1] gives you the city name
It depend on if there is always a comma in the "Calle Gran Vía, 8", if not you can use instead list=adress.split(",")[-2].split()
You might want to try this.
var address="Calle Gran Vía, 8, 28013 Madrid, Spain";
var splits = address.split(',')[2].trim().split(' ');
var newAdd = {
city : splits[1],
postal_code : splits[0]
}
console.log(newAdd);

passing an array from express to jade client side javascript

I have this jade template:
html
head
script(type="text/javascript" src="https://www.google.com/jsapi")
script(type='text/javascript')
google.load("visualization", "1", {packages:["corechart"]});
google.setOnLoadCallback(drawChart);
function drawChart() {
var data = google.visualization.arrayToDataTable([
['Date', 'Tin_A' ],
- each datapoint in myData
"[" + datapoint.date + "," + datapoint.value + "],"
]);
var options = {
title: 'bla'
};
var chart = new google.visualization.LineChart(document.getElementById('chart_line'));
chart.draw(data, options);
}
body
h1= title
#chart_line
and I'm using this call to render the jade template in express/node.js:
app.get('/', function(req, res){
sensors.findSensorAllData(2, 2, function (error,emps){
console.log(emps);
res.render('tmp', {
title : 'Temperatures in a row',
myData : emps
});
});
});
the output of the console.log(emps) is:
[ { _id: 524b8642028e167fb0e3661d,
sensor_id: 2,
value: 49,
date: Tue Oct 01 2013 20:34:40 GMT-0600 (CST) },
{ _id: 524b863d028e167fb0e3661c,
sensor_id: 2,
value: 19,
date: Tue Oct 01 2013 20:34:35 GMT-0600 (CST) } ]
after the rendering occour, I expect to have the values within the javascrip in the jade template... but It won't work. I get only the same lines in plain text, as if the line - each datapoing in myData would have no meaning... what am I doing wrong? Thanks
--- Edit:
Everything works fine if I replace the lines
- each datapoint in myData
"[" + datapoint.date + "," + datapoint.value + "],"
with
['2004', 20],
['2005', 30],
['2006', 40]
I think you may be accidentally injecting a String instead of an Array because of the quotes around the brackets:
- each datapoint in myData
"[" + datapoint.date + "," + datapoint.value + "],"
I'm not very familiar with Jade, but I think you may want to do the following instead:
- each datapoint in myData
[#{datapoint.date}, #{datapoint.value}],
Also, in the sample data you gave that works, you are only using the year portion of the Date, but the contents of the datapoint.date property may be a full Date object, I'm not sure if that is what you want for this use.
See this question's chosen answer for why what you're trying to do doesn't work. (JADE + EXPRESS: Iterating over object in inline JS code (client-side)?)
Basically, as soon as you hit the script tag, you're telling the Jade parser to handle things in raw form, and no further processing is done. What you really want to do is redo the script tag for your code like follows:
- if (typeof(pins) != "object")
!= "<script type='text/javascript'>"
!= "google.load('visualization', '1', {packages:['corechart']});
!= "google.setOnLoadCallback(drawChart);
!= "function drawChart() {
!= "var data = google.visualization.arrayToDataTable([
!= "['Date', 'Tin_A' ],"
- forEach datapoint in myData
!= "[" + datapoint.date + "," + datapoint.value + "],"
!= "]);"
!= "var options = {"
!= "title: 'bla'"
!= "};"
!= "var chart = new google.visualization.LineChart(document.getElementById('chart_line'));"
!= "chart.draw(data, options);"
!= "}"
Try this, but I'm fairly certain it should work.
PS: The link above also (I believe) clearly states why the previous answer should be incorrect, as you can't have that kind of template placeholder interpolation inside of Jade script tags.
Ugly alert:
script
...
var data = google.visualization.arrayToDataTable(['Date', 'Tin_A' ].concat(!{JSON.stringify(myData.map(function(i) { return [ i.date, i.value ] })) }));

Categories

Resources