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

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>

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)

Regex For Checking Duplicates

I am writing a HTML/JS bingo game. It needs to have functionality that lets users specify their own bingo board by entering a string that needs to be validated using a regex. The specifications are as follows:
The string format will be
B(15,9,8,7,14)I(25,21,20,22,29)N(38,41,f,34,31)G(60,57,48,56,49)O(69,70,72,64,71)
where B(15,9,8,7,14) means that the B column on the board contains
15, 9, 8, 7, and 14. I(25,21,20,22,29) means the I column contains
25, 21, 20, 22 and 29. And so forth. 'f' is used in the string to
represent the free space.
So far I have:
var string = /(B|b)(((1[0-5]|[1-9]),?){5})(I|i)(((1[6-9]|2[0-9]|30),?){5})(N|n)(((3[1-9]|4[0-5]),){2}(F|f),((3[1-9]|4[0-5]),?){2})(G|g)(((4[6-9]|5[0-9]|60),?){5})(O|o)(((6[1-9]|7[0-5]),?){5})/g;
Which validates the above example but doesn't check for duplicates. I.e., B(15,15,8,7,14)... should fail. I think negative lookahead is the right tool to use but i'm unsure how to use it in this context
Extract numbers from string into an Array, and check for duplicates:
var bingoStrings = [
"B(15,9,8,7,14)I(25,21,20,22,29)N(38,41,f,34,31)G(60,57,48,56,49)O(69,70,72,64,71)",
"B(15,15,8,7,14)I(25,21,20,22,29)N(38,41,f,34,31)G(60,57,48,56,49)O(69,70,72,64,71)"
];
bingoStrings.forEach(bingoString => {
var bingoArray = bingoString.match(/\d+/g);
var hasDuplicates = bingoArray.some(number =>
bingoArray.indexOf(number) !== bingoArray.lastIndexOf(number)
);
console.log(bingoString);
console.log("has " + (hasDuplicates?"":"no ") + "duplicates\n");
});
As a function:
var bingoStrings = [
"B(15,9,8,7,14)I(25,21,20,22,29)N(38,41,f,34,31)G(60,57,48,56,49)O(69,70,72,64,71)",
"B(15,15,8,7,14)I(25,21,20,22,29)N(38,41,f,34,31)G(60,57,48,56,49)O(69,70,72,64,71)"
];
hasDuplicates = (bingoString) =>
bingoString
.match(/\d+/g)
.some((number, index, bingoArray) =>
bingoArray.indexOf(number) !== bingoArray.lastIndexOf(number)
)
;
bingoStrings.forEach(bingoString => {
console.log(bingoString);
console.log("has " + (hasDuplicates(bingoString)?"":"no ") + "duplicates\n");
});

Get the middle part after spliting by two delemeter in a string using regex

Here is my String
Hey, Mounty Camp booking for {product} is Confirmed.%0A%0AHere are the details:%0ACamp Name: {product}%0ALocation: {location}%0AType: {roomType}%0ACheckIn : {checkIn}%0ACheckOut : {checkOut}%0AGuests: {guests}%0AAddress: {address}%0AGoogle Maps Location: {mapLink}%0A%0ANet Amount: {netAmount}%0AAdvance: {advanceAmount}%0APay at Camp: {pendingAmount}
Now I need the only middle of {} these two in an array
Here I have tried by this /{(.*?)} regular expressing but it is not working. Only it is giving the first middle part not entirely what I want.
Add g (global) flag to regex and after use this code :
var str = '%0A%0AHere are the details:%0ACamp Name: {product}%0ALocation: {location}%0AType: {roomType}%0ACheckIn : {checkIn}%0ACheckOut : {checkOut}%0AGuests: {guests}%0AAddress: {address}%0AGoogle Maps Location: {mapLink}%0A%0ANet Amount: {netAmount}%0AAdvance: {advanceAmount}%0APay at Camp: {pendingAmount}';
var result = str.match(/{(.*?)}/g).map(function (val) {
val = val.replace(/{/g, '');
return val.replace(/}/g, '');
});
console.log(result);

Replace part of a string with anchor tag

I have a sentence: "This is a sentence with a link1, also it has link2"
It is in p tag.
I also have data when it starts and when it ends, respectively:
[{ start: 26, end: 31 }, { start: 45, end: 50 }]
Is it possible to replace these parts of the sentence with anchor tags having upper data available. As a result it would be a something like this:
<p>This is a sentence with a <a>link1</a>, also it has <a>link2</a></p>
Thanks!
Building a string of HTML and/or using dangerouslySetInnerHTML is not the correct solution here. To solve this the "React way," consider that this structure:
<p>This is a sentence with a <a>link1</a>, also it has <a>link2</a></p>
...could be generated with this React component (this is more verbose than necessary, just for clarity's sake):
const LinkText = () => {
const parts = [
'This is a sentence with a ',
<a>{'link1'}</a>,
', also it has ',
<a>{'link2'}</a>,
];
return <p>{parts}</p>;
};
With that in mind, the question is this: How do we generate the parts array from a string and an array of link positions? The solution is to iterate over the array of link positions and, in each iteration, push onto the array 1) The "non-link" text between the previous link position's end (or the start of the string), and 2) an <a> containing the current link position's start and end. A simple, working implementation looks like this:
const LinkText = ({ text, linkPositions }) => {
const parts = [];
let currentIndex = 0;
linkPositions.forEach(({ start, end }) => {
if (currentIndex < start) {
parts.push(text.slice(currentIndex, start));
}
parts.push(<a>{text.slice(start, end)}</a>);
currentIndex = end;
});
if (currentIndex < text.length - 1) {
parts.push(text.slice(currentIndex)); // Remaining text after the last link
}
return <p>{parts}</p>;
};
const text = 'This is a sentence with a link1, also it has link2';
const links = [{ start: 26, end: 31 }, { start: 45, end: 50 }];
ReactDOM.render(<LinkText text={text} linkPositions={links}/>, document.querySelector('div'));
a{color:blue}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div></div>
The best way to do that is using IDs and JS.
So, for example, your HTML would look like this:
<p>This is a sentence with a <a id="linkOne">link1</a>, also it has <a id="linkTwo">link2</a></p>
And then your JS would call this:
document.getElementById("linkOne").href = "http://linkOne.com"
document.getElementById("linkTwo").href = "http://linkTwo.com"
You can achieve that with str.replace with a regex.
as #Dupocas said, React will escape html tags, so you will need to use dangerouslySetInnerHTML
const str = 'This is a sentence with a link1, also it has link2';
const result = str.replace(/link(\d)/g, (match, linkId) => {
return `${match}`;
});
console.log(result);
You can use this snippet to replace the text at the specific places into links. I have sorted it in a descending order based on the start since changing it from the end of the string is easier.
document.getElementById("convert").addEventListener("click", convertLinks);
addEventListener
function convertLinks() {
var pTag = document.getElementById("test")
var text = pTag.innerHTML
posData = [{
start: 26,
end: 31
}, {
start: 45,
end: 50
}]
posData = posData.sort(function(pos1, pos2) {
return pos2.start - pos1.start
})
for (pos of posData) {
text = replaceLink(text, pos)
}
pTag.innerHTML = text;
}
function replaceLink(text, pos) {
return text.slice(0, pos.start) + "<a>" + text.slice(pos.start, pos.end) + "</a>" + text.slice(pos.end)
}
a {
color: blue
}
<p id="test">This is a sentence with a link1, also it has link2</p>
<button id="convert"> Convert Links </button>

Extract numeric values with inch notation in a string and return larger one

I am trying to extract size information from products having names like.
Product A 30" Metalic Grey
Product B 31.50" Led 54 watt
Product C 40"-60" Dark Green
My current code to fetch size information is
var product_name = $(this).text();
product_name.split('"')[0].slice(-2);
I am having difficulty to deal sizes with points for example 31.50".
Is there a better way to extract sizes from above example product names also for product names like third product with size range it needs to return the bigger numeric value which is 60.
If you want to get all sizes from a string, you can use the regular expression [\d.]+(?="):
var sizes = text.match(/[\d.]+(?=")/g);
This will return an array of strings, such as ["31.50"] or ["40", "60"].
You can further process the array, e.g. convert the elements to numbers:
sizes = sizes.map(Number); // ["31.50"] -> [31.50]
and/or get the maximum value:
var maxSize = Math.max.apply(null, sizes); // ["40", "60"] -> 60
What about this?
product_name.replace(/^.*?([\d\.]+)"[^"]*$/, '$1');
You'll need to separate at the first " symbol, then find the last space before it:
var product_name = $(this).text();
var size = product_name.split('"')[0];
var space = product_name.lastIndexOf(' ');
size.slice(space, -1);
That'll give you everything from ", then back to the space before it.
Here is an example that will use a regex to find the strings then return the largest, run through each example string:
var t = [
'Product A 30" Metalic Grey',
'Product B 31.50" Led 54 watt',
'Product C 40"-60" Dark Green'
]
var getMaxInches = function(str) {
var inches = str.match(/[0-9]+(\.[0-9]+)?"/g); // The inches format pattern
var maxMeasure = 0;
for(i in inches){
var measure = (inches[i]).slice(0,-1); // remove the trailing "
maxMeasure = Math.max(Number(measure),maxMeasure);
}
return maxMeasure;
}
for(var i in t) alert(getMaxInches(t[i]));

Categories

Resources