I am new to JavaScript so I am struggling to even know where to start. Please can someone help me.
I have this array of ingredients:
const ingris = [
"1 cup heavy cream",
"8 ounces paprika",
"1 Chopped Tomato",
"1/2 Cup yogurt",
"1 packet pasta ",
"1/2 teaspoon freshly ground black pepper, divided",
]
I am trying to take out for example the 1 cup or 1/2 teaspoon (first 2 words of the array) and add it to a new array of objects like below:
const ShoppingList = [
{
val: "heavy cream",
amount: "1 cup",
},
{
val: "Tomato",
amount: "1 Chopped ",
},
{
val: "yogurt",
amount: "1/2 Cup",
},
];
Probably I would try to use .map() first iterate through the array of strings and convert it into an new array of objects. On each iteration you can .split() the string by spaces and most probably the first 2 elements of the array can be the amount property and the rest is the value.
See from the documentations:
The map() method creates a new array populated with the results of calling a provided function on every element in the calling array.
The split() method divides a String into an ordered list of substrings, puts these substrings into an array, and returns the array. The division is done by searching for a pattern; where the pattern is provided as the first parameter in the method's call.
Try as the following:
const ingris = [
"1 cup heavy cream",
"8 ounces paprika",
"1 Chopped Tomato",
"1/2 Cup yogurt",
"1 packet pasta",
"1/2 teaspoon freshly ground black pepper, divided",
];
const result = ingris.map(e => {
const split = e.split(' ');
const amount = `${split[0]} ${split[1]}`;
return { val: e.replace(`${amount} `, ''), amount };
});
console.log(result);
Probably you need to add fallback once you have different format of input strings, like checking if you have at least 3 words in that string.
Using Array#map to map the given array to a new one. Split the string to an arry at the spaces. return a new object with the first 2 array-elements as val and the others as amount. For gettuing the last elements use Array#slice and Array#join with a space as glue to connect them to a string.
const ingris = [
"1 cup heavy cream",
"8 ounces paprika",
"1 Chopped Tomato",
"1/2 Cup yogurt",
"1 packet pasta ",
"1/2 teaspoon freshly ground black pepper, divided",
];
let result = ingris.map(str => {
let arr = str.split(' ');
return {val: arr[0] + ' ' + arr[1], amount: arr.slice(2).join(' ')};
});
console.log(result);
There are multiple approach but using Array.prototype.map to loop over the array, String.prototype.split to turn each string into a array inside the loop, Array.prototype.slice() to get a slice from the array will help you create what you need. But keep in mind that it currently always get the first 2 words and the words after that. So your ingredients have to be the same way every time
const ingris = [
"1 cup heavy cream",
"8 ounces paprika",
"1 Chopped Tomato",
"1/2 Cup yogurt",
"1 packet pasta ",
"1/2 teaspoon freshly ground black pepper, divided",
];
const shoppingList = ingris.map(ingredient => {
const splitIngredient = ingredient.split(' ');
const amount = splitIngredient.slice(0, 2).join(' ');
const val = splitIngredient.slice(2, splitIngredient.length).join(' ');
return { val, amount };
});
console.log(shoppingList);
Related
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)
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>
For example, I have user input any string: "1st", "2nd", "third", "fourth", "fifth", "9999th", etc. These are just examples, the user can input any string.
I want to map this to integer cardinality:
"1st" -> 0
"2nd" -> 1
"third" -> 2
"fourth" -> 3
"fifth" -> 4
"9999th" -> 9998
So I need some kind of function where:
function mapCardinality(input: string): number{
let numberResult:number = ??
return numberREesult;
}
and I can call it like this:
console.log(
mapCardinality("1st"), // print 0
mapCardinality("2nd"), // print 1
mapCardinality("third"), // print 2
mapCardinality("fourth"), // print 3
mapCardinality("fifth"), // print 4
mapCardinality("9999th") // print 9998
);
Just look it up in an array or parse it as number:
const mapCardinality = c => {
const pos = ["1st", "2nd", "third", "fourth", "fifth"].indexOf(c);
return pos === -1 ? parseInt(c, 10) - 1 : pos;
};
I'd first ask what are the suffixes for all of the inputs?
'nd', 'rd', 'st', 'th' (most numbers)
If they enter an integer with the above prefixes then you could write the following function:
const getInteger = input => input.slice(0, -2);
const num = getInteger('999th');
console.log(num); // prints "999"
If they enter the elongated variant, it becomes much more complex, especially when it comes to typos, lack of spaces, etc. One way could be to map single digit words ('one', 'two', etc), tens ('ten', 'twenty', etc'), hundreds, thousands, and so on instead of every number imaginable. I would then parse and find matching words to give a result. That being said it is still limiting. I would strongly suggest limiting user input formats. Why can't the user input an integer?
const cardinalDictionary = {
'zero': 0,
'one': 1,
...,
'twenty',
...,
'hundred': 100,
'thousand': 1000,
};
How can I split a list of strings based on quantity and name?
For example if I have string str that looks like the following:
5 apples
7x pine apples
10 oranges
14x corn on the cob
apple pie
I could do,
var list = str.split(/\r?\n/);
So now I have each line in an array list but now I still need to get the quantity and name from each element in the list.
For list[0] which is '5 apples' I could do,
var breakdown = list[0].split(' ');
For list[1] I'd have to remove the 'x' from '7x' and it would incorrectly be split into 3 rather than just the quantity and name , etc.
For 'apple pie' the quantity should be 1.
The expected result is always,
breakdown[0]: quantity
breakdown[1]: name
How can I get the quantity and name regardless of how it's entered?
A regex on each line would do it. This follows with a second .map() to convert the numeric (or empty) string to a number.
var data = `5 apples
7x pine apples
10 oranges
14x corn on the cob
apple pie`;
var result = data.split(/\s*?(?:\r?\n)+\s*/g).map(s =>
/^(?:(\d+)x?\s+)?(.+)$/.exec(s).slice(1)
).map(([q, d]) => [+q || 1, d]);
console.log(result);
It could actually be done with just a regex too, if you include the m modifier.
var data = `5 apples
7x pine apples
10 oranges
14x corn on the cob
apple pie`;
var re = /^(?:(\d+)x?\s+)?(.+)$/gm;
var m;
var result = [];
while((m = re.exec(data))) {
result.push([+m[1] || 1, m[2]]);
}
console.log(result);
What's a good JavaScript library for searching a given string for a large list of names.
For example, given a list of 1000 politicians names find every instance in a string and wrap it in a span.
Priorities are performance with a growing list of names, and accuracy in determining difference between eg, "Tony Blair", "Tony Blair III".
For example, this:
["Tony Blair", "Margaret Thatcher", "Tony Blairite", "Tony Blair III", etc...]
"The best PM after Tony Blair was Margaret Thatcher."
Becomes:
"The best PM after <span class="mp">Tony Blair</span> was <span class="mp">Margaret Thatcher</span>."
var names = ['foo','bar'];
var content = "this foo is bar foobar foo ";
for (var c=0,l=names.length;c<l;c++) {
var r = new RegExp("\\b("+names[c]+")\\b","gi");
content = content.replace(r,'<span class="mp">$1</span>');
}