Finding the index to a non-specified character - javascript

Let's say for example I have a string
thisIsThisTuesday Day
I want to find the index of all the capital letters, test if there is a space before it, and if not insert one. I would need the index of each one.
At least from what I can see indexOf(String) will only produce the index of the first occurance of the character T/t
This :
for(i=0;i<str.length;i++){
let char=str[i];
if(isNaN(char*1)&&char==char.toUpperCase()){
y=str.indexOf(char);
console.log(char,y)
}
}
would produce the capital letters, and their indexes but will only display the first occurrence of the character in question. I feel pretty confident that the part I am missing is a for() loop in order to move the index iteration..but it escapes me.
Thank you in advance!

You can use a regex:
It matches any non-whitespace character followed by a capital letter and replaces it by the two characters with a space between.
const str = "thisIsThisTuesday Day";
const newstr = str.replace(/([^ ])([A-Z])/g, "$1 $2");
console.log(newstr);

You can use the following regular expression:
/(?<=\S)(?=[A-Z])/g
The replace will insert spaced between characters which are non-space followed by a capital letter.
See example below:
let str = "thisIsThisTuesday Day";
const res = str.replace(/(?<=\S)(?=[A-Z])/g, ' ');
console.log(res);
Note: As pointed out ?<= (positive lookbehind) is currently not be available in all browsers.

Actually, the String.indexOf function can take a second argument, specifying the character it should start searching from. Take a look at: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/indexOf
But, if you just want to find all capital letters and prefix them with a space character, if one is not found, there are many approaches, for example:
var str = "thisIsThisTuesday Day";
var ret = '';
for (var i=0; i<str.length; i++) {
if (str.substr(i, 1) == str.substr(i, 1).toUpperCase()) {
if ((i > 0) && (str.substr(i - 1,1) != " "))
ret += " ";
}
ret += str.substr(i,1);
}
After running this, ret will hold the value "this Is This Tuesday Day"

You could iterate over the string and check if each character is a capital. Something like this:
const s = 'thisIsThisTuesday Day';
const format = (s) => {
let string = '';
for (let c of s) {
if (c.match(/[A-Z]/)) string += ' ';
string += c;
}
return string;
};
console.log(format(s));
Or alternatively with reduce function:
const s = 'thisIsThisTuesday Day';
const format = (s) => s.split('').reduce((acc, c) => c.match(/[A-Z]/) ? acc + ` ${c}` : acc + c, '');
console.log(format(s));

Related

How to remove delimiters from a given RegEx?

my input:
str = "User-123"
o/p:
name: User
id: 123
another input:
str = "User 123"// current this works with my regex.
o/p: As above
other possible inputs:
str = "User:123"
str = "User/123"
str = "User:123"
code:
let m = value.match(/([a-z]+\s*\d+)\s+([a-z]+\s*\d+|\d+\s*[a-z]+)/i);
if (m) {return true}
else {return false}
if I have delimiters the above code return false as it does not find the match for the delimiters. I want to return true for all the scenarios listed above.
currently it removes only the whitespaces, how can I remove delimiters from this regex as well?
It looks like you just want to split on a non-alphanumeric character:
let inputs = [
"User:123",
"User/123",
"User:123",
"User-123",
"User 123"
]
for (i of inputs){
let [name, id] = i.split(/[^a-z0-9]/i)
console.log("name:", name, "id:", id)
}
You might consider simplifying your expression. Using capturing groups, you can simply add/remove any delimiters that you wish. For instance, this expression shows how you might use capturing group:
([A-z]+)(:|\/)([0-9]+)
Graph
This graph shows how the expression work:
Code
This code shows how to do so and does a basic benchmark with 1 million times repeat.
repeat = 1000000;
start = Date.now();
for (var i = repeat; i >= 0; i--) {
var string = 'User/123';
var regex = /([A-z]+)(:|\/)([0-9]+)/g;
var match = string.replace(regex, "$1$3");
}
end = Date.now() - start;
console.log(match + " is a match 💚 ");
console.log(end / 1000 + " is the runtime of " + repeat + " times benchmark test. 😳 ");

How to remove word in string based on array in Javascript when word's character length in string is fewer than in array?

I want to remove some word in string based on array. But the word's character length in string is fewer than in array. Is it possible to match it using regex and then replace it with empty string? If not, what is the alternatives?
I tried using regex to match the word, but i can't achieve it. I don't know how to make regex match minimum 3 character from the array.
array = ['reading', 'books'];
string = 'If you want to read the book, just read it.';
desiredOutput = 'If you want to the , just it.';
// Desired match
'reading' -> match for 'rea', 'read', 'readi', 'readin', 'reading'
'books' -> match for 'boo', 'book', 'books'
One option is to match 3 or more word characters starting at a word boundary, then use a replacer function to return the empty string if any of the words startsWith the word in question:
const array = ['reading', 'books'];
const string = 'If you want to read the book, just read it.';
const output = string.replace(
/\b\w{3,}/g,
word => array.some(item => item.startsWith(word)) ? '' : word
);
console.log(output);
The answer from CertainPerformance is better - easier to implement and to maintain but it's worth noting that - you can also generate a regex from the array.
The idea is simple enough - if you want to match r, re, rea, read, readi, readin, reading the regex for that is reading|readin|readi|read|rea|re|r. The reason you want the longest variation first is because otherwise the regex engine will stop at the first match in finds:
let regex = /r|re|rea|read/g
// ↑_________________
console.log( // |
"read".replace(regex, "")// |
// ↑___________________________|
)
So you can take a word and break it out in a this pattern to generate a regex from it
function allSubstrings(word) {
let substrings = [];
for (let i = word.length; i > 0; i--) {
let sub = word.slice(0, i);
substrings.push(sub)
}
return substrings;
}
console.log(allSubstrings("reading"))
With that you can simply generate the regex you need.
function allSubstrings(word) {
let substrings = [];
for (let i = word.length; i > 0; i--) {
let sub = word.slice(0, i);
substrings.push(sub)
}
return substrings;
}
function toPattern(word) {
let substrings = allSubstrings(word);
let pattern = substrings.join("|");
return pattern;
}
console.log(toPattern("reading"))
The final thing is to take an array and convert it to a regex. Which requires treating each word and then combining each individual regex into one that matches any of the words:
const array = ['reading', 'books'];
const string = 'If you want to read the book, just read it.';
//generate the pattern
let pattern = array
.map(toPattern) //first, for each word
.join("|"); //join patterns for all words
//convert the pattern to a regex
let regex = new RegExp(pattern, "g");
let result = string.replace(regex, "");
//desiredOutput: 'If you want to the , just it.';
console.log(result);
function allSubstrings(word) {
let substrings = [];
for (let i = word.length; i > 0; i--) {
let sub = word.slice(0, i);
substrings.push(sub)
}
return substrings;
}
function toPattern(word) {
let substrings = allSubstrings(word);
let pattern = substrings.join("|");
return pattern;
}
So, this is how you can generate a regular expression from that array. In this case, that works, but it's not guaranteed to, because there is a danger it could match something you don't want. For example, r will match any character, it doesn't necessarily need to be in a word that matches this.
const array = ['reading'];
const string = 'The quick brown fox jumps over the lazy dog';
// ^ ^
let pattern = array
.map(word => allSubstrings(word).join("|"))
.join("|");
let regex = new RegExp(pattern, "g");
let result = string.replace(regex, "");
console.log(result);
function allSubstrings(word) {
let substrings = [];
for (let i = word.length; i > 0; i--) {
let sub = word.slice(0, i);
substrings.push(sub)
}
return substrings;
}
Which is when it becomes more complicated, as you want to generate a more complicated pattern for each word. You generally want to match words, so you can use the word boundary character \b which means that the pattern for "reading" can now look like this:
\breading\b|\breadin\b|\breadi\b|\bread\b|\brea\b|\bre\b|\br\b
↑↑ ↑↑ ↑↑ ↑↑ ↑↑ ↑↑ ↑↑ ↑↑ ↑↑ ↑↑ ↑↑ ↑↑ ↑↑ ↑↑
In the interest of keeping the output at least somewhat readable, it can instead be put in a group and the whole group made to match a single word:
\b(?:reading|readin|readi|read|rea|re|r)\b
↑↑
||____ non-capturing group
So, you have to generate this pattern
function toPattern(word) {
let substrings = allSubstrings(word);
//escape backslashes, because this is a string literal and we need \b as content
let pattern = "\\b(?:" + substrings.join("|") + ")\\b";
return pattern;
}
Which leads us to this
const array = ['reading', 'books'];
const string = 'The quick brown fox jumps over the lazy dog. If you want to read the book, just read it.';
let pattern = array
.map(toPattern)
.join("|");
let regex = new RegExp(pattern, "g");
let result = string.replace(regex, "");
console.log(result);
function allSubstrings(word) {
let substrings = [];
for (let i = word.length; i > 0; i--) {
let sub = word.slice(0, i);
substrings.push(sub)
}
return substrings;
}
function toPattern(word) {
let substrings = allSubstrings(word);
let pattern = "\\b(?:" + substrings.join("|") + ")\\b";
return pattern;
}
This will suffice to solve your task. So it's possible to generate a regex. The final one looks like this:
/\b(?:reading|readin|readi|read|rea|re|r)\b|\b(?:books|book|boo|bo|b)\b/g
But most of the generation of it is spent trying to generate something that works. It's not a necessarily complex solution but as mentioned, the one suggested by CertainPerformance is better because it's simpler which means less chance of it failing and it would be easier to maintain for the future.
I don't know of a straight way to do it, but you can create your own regexp pattern, like so:
// This function create a regex pattern string for each word in the array.
// The str is the string value (the word),
// min is the minimum required letters in eac h word
function getRegexWithMinChars(str, min) {
var charArr = str.split("");
var length = charArr.length;
var regexpStr = "";
for(var i = 0; i < length; i++){
regexpStr +="[" + charArr[i] + "]" + (i < min ? "" : "?");
}
return regexpStr;
}
// This function returns a regexp object with the patters of the words in the array
function getStrArrayRegExWithMinChars(strArr, min) {
var length = strArr.length;
var regexpStr = "";
for(var i = 0; i < length; i++) {
regexpStr += "(" + getRegexWithMinChars(strArr[i], min) + ")?";
}
return new RegExp(regexpStr, "gm");
}
var regexp = getStrArrayRegExWithMinChars(searchArr, 3);
// With the given regexp I was able to use string replace to
// find and replace all the words in the string
str.replace(regexp, "");
//The same can be done with one ES6 function
const getStrArrayRegExWithMinChars = (searchArr, min) => {
return searchArr.reduce((wordsPatt, word) => {
const patt = word.split("").reduce((wordPatt, letter, index) => {
return wordPatt + "[" + letter + "]" + (index < min ? "" : "?");
},"");
return wordsPatt + "(" + patt + ")?";
}, "");
}
var regexp = getStrArrayRegExWithMinChars(searchArr, 3);
// With the given regexp I was able to use string replace to
// find and replace all the words in the string
str.replace(regexp, "");

JavaScript regexp, not getting all matches, what am I missing here? [duplicate]

This question already has answers here:
How can I match overlapping strings with regex?
(6 answers)
Closed 4 years ago.
Let's say for example I have this simple string
let str = '5+81+3+16+42'
Now if I want to capture each plus sign with both numbers around it.
My attempt was as follows:
let matches = str.match(/\d+\+\d+/g);
What I got with that is:
['5+81', '3+16']
Why is it not matching the cases between?
['5+81', '81+3', '3+16', '16+42']
Your regex has to fulfill the whole pattern which is \d+\+\d+. It will first match 5+81, then the next character is a + which the pattern can not match because it should start with a digit. Then it can match 3+16 but it can not match the following +42 anymore given you ['5+81', '3+16'] as the matches.
Without a regex, you might use split and a for loop and check if the next value exists in the parts array:
let str = '5+81+3+16+42'
let parts = str.split('+');
for (let i = 0; i < parts.length; i++) {
if (undefined !== parts[i + 1]) {
console.log(parts[i] + '+' + parts[i + 1]);
}
}
When using more a recent version of Chrome which supports lookbehinds, you might use lookarounds with capturing groups:
(?<=(\d+))(\+)(?=(\d+))
See the regex demo
const regex = /(?<=(\d+))(\+)(?=(\d+))/g;
const str = `5+81+3+16+42`;
let m;
while ((m = regex.exec(str)) !== null) {
if (m.index === regex.lastIndex) {
regex.lastIndex++;
}
console.log(m[1] + m[2] + m[3]);
}
When the regular expression engine completes one iteration of a match, it "consumes" the characters from the source string. The first match of 5+81 leaves the starting point for the next match at the + sign after 81, so the next match for the expression begins at the 3.
Split string by + delimiter and use .reduce() to create new array contain target result.
let str = '5+81+3+16+42';
let arr = str.split('+').reduce((tot, num, i, arr) => {
i+1 < arr.length ? tot.push(num+"+"+arr[i+1]) : '';
return tot;
}, []);
console.log(arr);
You can do it using split and reduce without making things complex with regex.
let str = '5+81+3+16+42';
const array = str.split('+');
const splited = []
array.reduce((a, b) => {
splited.push(a+'+'+b)
return b
})
console.log(splited);

Adding a whitespace in front of the first number in a string

I need a whitespace to be added in front of the first number of a string, unless there is one already and unless the number is the first character in the string.
So I wrote this JS code:
document.getElementById('billing:street1').addEventListener('blur', function() {
var value = document.getElementById('billing:street1').value;
var array = value.match(/\d{1,}/g);
if (array !== null) {
var number = array[0];
var index = value.indexOf(number);
if(index !== 0){
var street = value.substring(0, index);
var housenumber = value.substring(index);
if (street[street.length - 1] !== ' ') {
document.getElementById('billing:street1').value = street + ' ' + housenumber;
}
}
}
});
Fiddle
It works fine, but I feel like this can probably be done in a smarter, more compact way.
Also, JQuery suggestions welcome, I am just not very familiar with it.
Try this one :
const addSpace = (str) => {
return str.replace(/(\D)(\d)/, "$1 $2")
}
console.log(addSpace("12345")) // "12345"
console.log(addSpace("city12345")) // "city 12345"
console.log(addSpace("city")) // "city"
(\D) captures a non-digit
(\d) captures a digit
so (\D)(\d) means : non-digit followed by a digit
that we replace with "$1 $2" = captured1 + space + captured2
You can do it by using only regular expressions. For example:
var s = "abcde45";
if(!s.match(/\s\d/)){
s = s.replace(/(\d)/, ' $1');
}
console.log(s); // "abcde 45"
UPD : Of course, if you have string with a wrong syntax(e.g no numbers inside), that code wouldn't work.

RegEx: different initials format

I am using this piece of code to get the initials of the person's full name:
var name = "John Smith"; // for an example
var initials = name.match(/\b\w/g) || [];
initials = ((initials.shift() || '') + (initials.pop() || '')).toUpperCase();
// initials then returns "JS"
Now I need my initials to return the first letter of the first name and three letters of the last name ("JSMI" in the example above).
What should I alter in my regex in order to do that?
Also, if person would have two names (for example "John Michael Smith"), I need to get "JMSMI" as a result...
Any other solutions are welcome!
Try with Array#split() , substring() and Array#map
first you need split the string with space.
And get the single letter array[n-1] using sustring,
Then get the 3 letter on final argument of array
Map function iterate each word of your string
function reduce(a){
var c= a.split(" ");
var res = c.map((a,b) => b < c.length-1 ? a.substring(0,1) : a.substring(0,3))
return res.join("").toUpperCase()
}
console.log(reduce('John Michael Smith'))
console.log(reduce('John Smith'))
You may add a \b\w{1,3}(?=\w*$) alternative to your existing regex at the start to match 1 to 3 words chars in the last word of the string.
var name = "John Michael Smith"; //John Smith" => JSMI
var res = name.match(/\b\w{1,3}(?=\w*$)|\b\w/g).map(function (x) {return x.toUpperCase()}).join("");
console.log(res);
See the regex demo.
Regex details:
\b - a leading word boundary
\w{1,3} - 1 to 3 word chars (ASCII letters, digits or _)
(?=\w*$) - a positive lookahead requiring 0+ word chars followed with the end of string position
| - or
\b\w - a word char at the start of a word.
I tried to avoid capturing groups (and used the positive lookahead) to make the JS code necessary to post-process the results shorter.
Use split() and substr() to easily do this.
EDIT
Updated code to reflect the middle initial etc
function get_initials(name) {
var parts = name.split(" ");
var initials = "";
for (var i = 0; i < parts.length; i++) {
if (i < (parts.length - 1)) {
initials += parts[i].substr(0, 1);
} else {
initials += parts[i].substr(0, 3);
}
}
return initials.toUpperCase();
}
console.log(get_initials("John Michael Smith"));
My two cents with a reducer :)
function initials(name) {
return name.split(' ').reduce(function(acc, item, index, array) {
var chars = index === array.length - 1 ? 3 : 1;
acc += item.substr(0, chars).toUpperCase();
return acc;
}, '')
}
console.log(initials('John'));
console.log(initials('John Michael'));
console.log(initials('John Michael Smith'));
You may want use String.prototype.replace to drop following letters:
This regexp will match first 1 (or 3 for last name) letters in a word, and only keep it.
'John Smith'.replace(/(?:(\w)\w*\s+)|(?:(\w{3})\w*$)/g, '$1$2').toUpperCase()
const name = "John Michael Smith";
const initials = name.toUpperCase().split(/\s+/)
.map((x, i, arr) => x.substr(0, i === arr.length - 1 ? 3 : 1))
.join('');
console.log(initials);

Categories

Resources