Javascript to match substring and strip everything after it - javascript

I need to match a substring X within string Y and need to match X then strip everything after it in Y.

Code
var text1 = "abcdefgh";
var text2 = "cde";
alert(text1.substring(0, text1.indexOf(text2)));
alert(text1.substring(0, text1.indexOf(text2) + text2.length));
First alert doesn't include search text, second one does.
Explanation
I'll explain the second line of the code.
text1.substring(0, text1.indexOf(text2) + text2.length))
text1.substring(startIndex, endIndex)
This piece of code takes every character from startIndex to endIndex, 0 being the first character. So In our code, we search from 0 (the start) and end on:
text1.indexOf(text2)
This returns the character position of the first instance of text2, in text 1.
text2.length
This returns the length of text 2, so if we want to include this in our returned value, we add this to the length of the returned index, giving us the returned result!

If you're looking to match just X in Y and return only X, I'd suggest using match.
var x = "treasure";
var y = "There's treasure somewhere in here.";
var results = y.match(new RegExp(x)); // -> ["treasure"]
results will either be an empty array or contain the first occurrence of x.
If you want everything in y up to and including the first occurrence of x, just modify the regular expression a little.
var results2 = y.match(new RegExp(".*" + x)); // -> ["There's treasure"]

You can use substring and indexOf:
Y.substring(0, Y.indexOf(X) + X.length))
DEMO
Of course you should test beforehand whether X is in Y.

var index = y.indexOf(x);
y = index >= 0 ? y.substring(0, y.indexOf(x) + x.length) : y;

var X = 'S';
var Y = 'TEST';
if(Y.indexOf(X) != -1){
var pos = parseInt(Y.indexOf(X)) + parseInt(X.length);
var str = Y.substring(0, pos);
Y = str;
}
document.write(Y);

Related

How to search for "x" or "any number" in a string?

I ran into a bit of a block when wanting to make a dice rolling script.
The idea is that you can input any amount of arguments to roll any amount of dice, 2d6 + 1d4 + 5d2 - [...].
How can I make a script that finds all the xdy's and puts them into an array? It must be based on xdy instead of, for example, spaces for cases like 1d4+1d6-1d8.
You can extract the pairs of numbers with matchAll, like this:
let s = "1d4+1d6-1d8";
let x = Array.from(s.matchAll(/(-?\d+)d(\d+)/g));
console.log(x);
This gives you an array of triplets. Each triplet has the xdy pattern that was matched, the value of x and the value of y (as strings).
If you need the x and y values as numbers instead of strings, then add a mapper callback to the Array.from call:
let s = "1d4+1d6-1d8";
let x = Array.from(s.matchAll(/(-?\d+)d(\d+)/g), ([s, x, y]) => [+x, +y]);
console.log(x);
Start with creating a function that takes a string (like "2d6") and returns a number
function rollDice(dicePattern) {
var num = ... //use regex or indexOf ("d");
var sides = ... //regex or substring(indexOf("d) + 1);
var sum = 0;
for (let i = 0; i <num;i++) {
//generate a random, multiply by sides, get the int value, and add it to sum
}
return sum;
}
Then split the string and call this on all your dice roll patterns
As stated in the comments, regex is your friend. And regexr is super handy! https://regexr.com/4mvj6
\d will match a digit character, so \dd\d will match any number + 'd' + number combinations in your string.
// '2d6 + 1d4 + 5d2' => ['2d6', '1d4', '5d2']
function parseRolls(rollsString)
{
if (rollsString)
{
return rollsString.match(/\dd\d/g);
}
return [];
}
I'm not completely sure what the exact format you're expecting is, but this returns an array of objects with properties x and y given a string of xdys delimited by plusses or minuses.
function parseDiceRoll(rollString) {
let rollBlocks = rollString.split(/[\+\-]/);
return rollBlocks.map(roll => {
let [x, y] = roll.split("d");
return {x, y};
});
}
console.log(parseDiceRoll("3d6+4d8-2d12"));

How can I use a Regular Expression to replace everything except specific words in a string with Javascript

Imagine you have a string like this: "This is a sentence with words."
I have an array of words like $wordList = ["sentence", "words"];
I want to highlight words that aren't on the list. Which means I need to find and replace everything else and I can't seem to crack how to do that (if it's possible) with RegEx.
If I want to match the words I can do something like:
text = text.replace(/(sentence|words)\b/g, '<mark>$&</mark>');
(which will wrap the matching words in "mark" tags and, assuming I have some css for <mark>, highlight them) which works perfectly. But I need the opposite! I need it to basically select the entire string and then exclude the words listed. I've tried /^((?!sentence|words)*)*$/gm but this gives me a strange infinity issue because I think it's too open ended.
Taking that original sentence, what I would hope to end up with is "<mark> This is a </mark> sentence <mark> with some </mark> words."
Basically wrapping (via replace) everything except the words listed.
The closest I can seem to get is something like /^(?!sentence|words).*\b/igm which will successfully do it if a line starts with one of the words (ignoring that entire line).
So to summarize: 1) Take a string 2) take a list of words 3) replace everything in the string except the list of words.
Possible? (jQuery is loaded for something else already, so raw JS or jQuery are both acceptable).
Create the regex from the word list.
Then do a string replace with the regex.
(It's a tricky regex)
var wordList = ["sentence", "words"];
// join the array into a string using '|'.
var str = wordList.join('|');
// finalize the string with a negative assertion
str = '\\W*(?:\\b(?!(?:' + str + ')\\b)\\w+\\W*|\\W+)+';
//create a regex from the string
var Rx = new RegExp( str, 'g' );
console.log( Rx );
var text = "%%%555This is a sentence with words, but not sentences ?!??!!...";
text = text.replace( Rx, '<mark>$&</mark>');
console.log( text );
Output
/\W*(?:\b(?!(?:sentence|words)\b)\w+\W*|\W+)+/g
<mark>%%%555This is a </mark>sentence<mark> with </mark>words<mark>, but not sentences ?!??!!...</mark>
Addendum
The regex above assumes the word list contains only word characters.
If that's not the case, you must match the words to advance the match position
past them. This is easily accomplished with a simplified regex and a callback function.
var wordList = ["sentence", "words", "won't"];
// join the array into a string using '|'.
var str = wordList.join('|');
str = '([\\S\\s]*?)(\\b(?:' + str + ')\\b|$)';
//create a regex from the string
var Rx = new RegExp( str, 'g' );
console.log( Rx );
var text = "%%%555This is a sentence with words, but won't be sentences ?!??!!...";
// Use a callback to insert the 'mark'
text = text.replace(
Rx,
function(match, p1,p2)
{
var retStr = '';
if ( p1.length > 0 )
retStr = '<mark>' + p1 + '</mark>';
return retStr + p2;
}
);
console.log( text );
Output
/([\S\s]*?)(\b(?:sentence|words|won't)\b|$)/g
<mark>%%%555This is a </mark>sentence<mark> with </mark>words<mark>, but
</mark>won't<mark> be sentences ?!??!!...</mark>
You could still perform the replacement on the positive matches, but reverse the closing/opening tag, and add an opening tag at the start and a closing one at the end of the string. I use here your regular expression which could be anything you want, so I'll assume it matches correctly what needs to be matched:
var text = "This is a sentence with words.";
text = "<mark>" + text.replace(/\b(sentence|words)\b/g, '</mark>$&<mark>') + "</mark>";
// If empty tags bother you, you can add:
text = text.replace(/<mark><\/mark>/g, "");
console.log(text);
Time Complexity
In comments below someone makes a point that the second replacement (which is optional) is a waste of time. But it has linear time complexity as is illustrated in the following snippet which charts the duration for increasing string sizes.
The X axis represents the number of characters in the input string, and the Y-axis represents the number of milliseconds it takes to execute the replacement with /<mark><\/mark>/g on such input string:
// Reserve memory for the longest string
const s = '<mark></mark>' + '<mark>x</mark>'.repeat(2000);
regex = /<mark><\/mark>/g,
millisecs = {};
// Collect timings for several string sizes:
for (let size = 100; size < 25000; size+=100) {
millisecs[size] = test(15, 8, _ => s.substr(0, size).replace(regex, ''));
}
// Show results in a chart:
chartFunction(canvas, millisecs, "len", "ms");
// Utilities
function test(countPerRun, runs, f) {
let fastest = Infinity;
for (let run = 0; run < runs; run++) {
const started = performance.now();
for (let i = 0; i < countPerRun; i++) f();
// Keep the duration of the fastest run:
fastest = Math.min(fastest, (performance.now() - started) / countPerRun);
}
return fastest;
}
function chartFunction(canvas, y, labelX, labelY) {
const ctx = canvas.getContext('2d'),
axisPix = [40, 20],
largeY = Object.values(y).sort( (a, b) => b - a )[
Math.floor(Object.keys(y).length / 10)
] * 1.3; // add 30% to value at the 90th percentile
max = [+Object.keys(y).pop(), largeY],
coeff = [(canvas.width-axisPix[0]) / max[0], (canvas.height-axisPix[1]) / max[1]],
textAlignPix = [-8, -13];
ctx.translate(axisPix[0], canvas.height-axisPix[1]);
text(labelY + "/" + labelX, [-5, -13], [1, 1], false, 2);
// Draw axis lines
for (let dim = 0; dim < 2; dim++) {
const c = coeff[dim], world = [c, 1];
let interval = 10**Math.floor(Math.log10(60 / c));
while (interval * c < 30) interval *= 2;
if (interval * c > 60) interval /= 2;
let decimals = ((interval+'').split('.')[1] || '').length;
line([[0, 0], [max[dim], 0]], world, dim);
for (let x = 0; x <= max[dim]; x += interval) {
line([[x, 0], [x, -5]], world, dim);
text(x.toFixed(decimals), [x, textAlignPix[1-dim]], world, dim, dim+1);
}
}
// Draw function
line(Object.entries(y), coeff);
function translate(coordinates, world, swap) {
return coordinates.map( p => {
p = [p[0] * world[0], p[1] * world[1]];
return swap ? p.reverse() : p;
});
}
function line(coordinates, world, swap) {
coordinates = translate(coordinates, world, swap);
ctx.beginPath();
ctx.moveTo(coordinates[0][0], -coordinates[0][1]);
for (const [x, y] of coordinates.slice(1)) ctx.lineTo(x, -y);
ctx.stroke();
}
function text(s, p, world, swap, align) { // align: 0=left,1=center,2=right
const [[x, y]] = translate([p], world, swap);
ctx.font = '9px courier';
ctx.fillText(s, x - 2.5*align*s.length, 2.5-y);
}
}
<canvas id="canvas" width="600" height="200"></canvas>
For each string size (which is incremented with steps of 100 characters), the time to run the regex 15 times is measured. This measurement is repeated 8 times and the duration of the fastest run is reported in the graph. On my PC the regex runs in 25µs on a string with 25 000 characters (consisting of <mark> tags). So not something to worry about ;-)
You may see some spikes in the chart (due to browser and OS interference), but the overall tendency is linear.
Given that the main regex has linear time complexity, the overall time complexity is not negatively affected by it.
However that optional part can be performed without regular expression as follows:
if (text.substr(6, 7) === '</mark>') text = text.substr(13);
if (text.substr(-13, 6) === '<mark>') text = text.substr(0, text.length-13);
Due to how JavaScript engines deal with strings (immutable), this longer code runs in constant time.
Of course, it does not change the overall time complexity, which remains linear.
I'm not sure if this will work for every case, but for the given string it does.
let s1 = "This is a sentence with words.";
let wordList = ["sentence", "words"];
let reg = new RegExp("([\\s\\S]*?)(" + wordList.join("|") + ")", "g");
console.log(s1.replace(reg, "<mark>$1</mark>$2"))
Do it the opposite way: Mark everything and unmark the matched words you have.
text = `<mark>${text.replace(/\b(sentence|words)\b/g, '</mark>$&<mark>')}</mark>`;
Negated regex is possible but inefficient for this. In fact regex is not the right tool. The viable method is to go through the strings and manually construct the end string:
//var text = "This is a sentence with words.";
//var wordlist = ["sentence", "words"];
var result = "";
var marked = false;
var nextIndex = 0;
while (nextIndex != -1) {
var endIndex = text.indexOf(" ", nextIndex + 1);
var substring = text.slice(nextIndex, endIndex == -1 ? text.length : endIndex);
var contains = wordlist.some(word => substring.includes(word));
if (!contains && !marked) {
result += "<mark>";
marked = true;
}
if (contains && marked) {
result += "</mark>";
marked = false;
}
result += substring;
nextIndex = endIndex;
}
if (marked) {
result += "</mark>";
}
text = result;

how to count digits of a phonenumber(included 0) in javascript?

I am trying to write a javascript , And want to count digits of a var str,
in the code below var str is 6 digits (012345), but when i run this code it is showing answer 4. i tried to search on google but answer not found;
how to get correct answer and fix it ?
my code
var str = 012345;
var x = String(str);
var n = x.length;
document.getElementById("demo").innerHTML = "var str is[" + n + "] Digits";
Initialize you phone number as string using quotes.
var str = '0123213'
And use length property to get its length
If you were to actually have a mixed letter/number string from which you wanted to get the number of digits you could use a regex. match creates an array of all the matches in the string - in this case \d, a digit (g says to check the whole of the string, not give up the search when the first digit has been found.) You can then check the length of the returned array.
'01xx2s3eg345'.match(/\d/g).length; // 7
try replacing the code with:
var str = "012345";
var n = str.length;
document.getElementById("demo").innerHTML = "var str is[" + n + "] Digits";

Dynamically wrap index with array using while loop

My code speaks for me, I want to wrap array index with another array dynamically (with a loop).
The following code does not work. Please, help me to convert this "x" string to JavaScript code or to find the right way to get the result.
var x = parentTasks[j];
while(x){
x = parentTasks + '[' + numbers + '[' + x + ']]';
}
Later "x" will become undefined, so then loop should stop.
What I expect:
Example when loop is iterated for 1st time:
parentTasks[numbers[parentTasks[j]]]
Example when loop is iterated for 2nd time:
parentTasks[numbers[parentTasks[numbers[parentTasks[j]]]]]
I did it by my self. Here is a solution:
var x = parentTasks[j];
var z = 0
while ( z++ < 2 ) {
x = 'parentTasks[numbers[' + x + ']]';
console.log(eval(x));
}

Position of character in a string

I have a string :
var str = "u12345a45";//position is 7 here
now i want the position of 'a'(alphabet) in that string
similarly i have few more string like this:
var str1 = "u1234567a45";//position is 9 here
var str2 = "u12345b4";//position of b is 7 here
var str3 = "u123c";//position of c is 5 here
var str4 = "u3d45";//position of d is 2 here
Now what i thought of doing is , just searching the string from last and know the occurrence of any alphabet in that strings for once.
Note:It might be any alphabet in a string like this:
var str5 = "u2233b45";//position of b is 6 here
var str6 = "u22333f45";//position of f is 7 here
any help will be appreciated .
thanks.
As simple as
str.indexOf('a') + 1
for an arbitrary non-digit character it could be
str.match(/\D/).index + 1
for the last non-digit character followed by 0..inf digit characters:
str.match(/\D\d*$/).index + 1
just use indexOf method.
var str1 = "1234567a45";
alert(str1.indexOf("a") + 1); // alerts 8
You can use JavaScript's indexOf method.
var pos1 = str1.indexOf('a'); // will equal 7
var pos2 = str2.indexOf('a'); // will equal 5
var pos3 = str3.indexOf('a'); // will equal 3
var pos4 = str4.indexOf('a'); // will equal 0
Here's a small Codesnippet that should solve your Problem.
var str1= "12345t45";
var str1Length = str1.length;
for(var a=0; a<str1Length ;a++){
if(isNaN(str1.substring(a,a+1))){
alert(str1.substring(a,a+1)+' at Position : '+(a+1));
}
}
I loope through the string and check if the actual position is a letter or a number. If it's a Letter i write him into the alert. In this Case the Alert says : 't' at Position : 6
Regards, Miriam

Categories