RegEx: different initials format - javascript

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);

Related

Insert Spaces into string at an index

I'm trying to do this Codewars problem.
Task
In this simple Kata your task is to create a function that turns a string into a Mexican Wave. You will be passed a string and you must return that string in an array where an uppercase letter is a person standing up.
Rules
The input string will always be lower case but maybe empty.
If the character in the string is whitespace then pass over it as if it was an empty seat.
Example
wave("hello") => ["Hello", "hEllo", "heLlo", "helLo", "hellO"]
My code so far is hosted on this repl.it
My thought process is as follows:
Turn argument into array
manipulate each index of the array at index and then readjust previous index to make a wave pattern
turn array into string
reinsert spaces before logging it to console and restarting the loop
I'm pretty stuck and my mind is stuck on how to use
for(var j = 0; j < indexSpaceNumber.length; j++){
//join and add in the spaces at their former index before returning string
strToArray[indexSpaceNumber[j]].slice(0, " ");
}
to insert the spaces into the string.
If there's any guidance or tips it would be much appreciated. I feel like I'm close, but so frustratingly far.
The main idea would be:
Iterate the characters
Replace the character in the original string with an uppercase version
You can use Array.from() to convert the string to an array, and map each item to a new string. If the character is a space return something falsy (en empty string in the example). After the creating the array, filter all falsy values:
const wave = str =>
Array.from(str, (c,i) => // convert the string to an array
// replace the character with an uppercase version in the original string
c === ' ' ?
''
:
`${str.substring(0, i)}${c.toUpperCase()}${str.substring(i + 1)}`
).filter(c => c)
const result = wave("hello")
console.log(result)
For string with spaces
function wave(str) {
let res = []
str.toLowerCase().split('').forEach((v, i) => {
if(v == ' ') return;
res.push( str.substr(0, i) + v.toUpperCase() + str.substr(i + 1) )
});
return res
}
console.log(wave("hello hello"))
I'd go recursive ;)
You know that for a string of length n you need an array of the same length. That's your exit condition.
You can use the length of the array at each iteration to work out the shape of the next string:
hello [] [Hello] 0: uppercase 1st char and append
hello [Hello] [Hello hEllo] 1: uppercase 2nd char and append
hello [Hello hEllo] [Hello hEllo heLlo] 2: uppercase 3rd char and append
...
const wave =
(str, arr = []) =>
str.length === arr.length
? arr
: wave
( str
, [ ...arr
, str.slice(0, arr.length)
+ str[arr.length].toUpperCase()
+ str.slice(arr.length + 1)
]
);
console.log(wave('hello'));
Go over each char in string and build
Slice str from start till current char + current char to upper case + Slice str from current char to end
const wave = str => {
const res = [];
for (let i = 0; i < str.length; i++) {
res.push(`${str.slice(0, i)}${str[i].toUpperCase()}${str.slice(i + 1)}}`);
}
return res;
};
console.log(wave("hi my name is rylan"));
// Alternate way to do with Array.splice
const wave2 = str => {
const res = [];
for (let i in str) {
const temp = Array.from(str);
temp.splice(i, 1, temp[i].toUpperCase());
res.push(temp)
}
return res.map(x => x.join(''));
};
console.log(wave2("hi my name is rylan"));

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, "");

RegEx replacing numbers greater than variable in Javascript

I have the string:
"selection1 selection2 selection3 selection4"
I am looking to remove all words that end in a number greater than a variable. For instance:
let str = "selection1 selection2 selection3 selection4";
let x = 2;
let regExp = RegExp(...);
let filtered = str.replace(regExp , ""); // should equal "selection1 selection2"
I came up with the following expression which selects all words that end in numbers greater than 29:
/(selection[3-9][0-9]|[1-9]\d{3}\d*)/gi
The result from this regEx on the string "selection1 selection 40" is [selection40]
I feel that I'm part of the way there.
Given that I'm dealing with single and double digit numbers and am looking to incorporate a variable, what regEx could help me alter this string?
You can use .replace with a callback:
let str = "selection5 selection1 selection2 selection3 selection4";
let x = 2;
let regex = /\s*\b\w+?(\d+)\b/g;
let m;
let repl = str.replace(regex, function($0, $1) {
return ($1 > x ? "" : $0);
}).trim();
console.log( repl );
Regex /\b\w+?(\d+)\b/g matches all the words ending with 1+ digits and captures digits in capture group #1 which we use inside the callback function to compare against variable x.
You can split by whitespace, then capture the group using Regex which gets the only the numeric part and filter it accordingly.
const str = "selection1 selection2 selection3 selection4";
const threshold = 2;
const pattern = /selection(\d+)/
const result = str
.split(' ')
.filter(x => Number(x.match(pattern)[1]) <= threshold)
.join(' ');
console.log(result);

Finding the index to a non-specified character

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));

check if any duplicate words exist in a string using regex

Using regex in javascript, how to check for the condition when duplicate words are present in a string?
The words could be located any where in the string:
Given: "but boys run fast boys are strong" or
"boys boys"
expected result: true, since there are 2 repeats of the word "boys"
\b matches word boundaries
\w+ will match 1 or more word characters
( ... ) creates a group for matches
\1 will match the contents of matching group #1.
Putting this together, you want a regex containing \b(\w+)\b.*\b\1\b
Plus appropriate backslash quoting, etc.
#guest27134 pointed out the above is not the complete solution, since the OP wanted true/false, not just the regex:
var result = a_string.match(/\b(\w+)\b.*\b\1\b/g) !== null
Or, even shorter, as suggested by O.P.:
var result = /\b(\w+)\b.*\b\1\b/g.test(myStr)
Taking into consideration that spaces are there after each word
var string = "but boys run fast boys are strong";
var strArray= string.split(" ");
var unique = [];
for(var i =0; i< strArray.length; i++)
{
eval(unique[strArray] = new Object());
}
If you know what word you're testing for duplicates you could use regular expressions like this str.match(/boys/g).length > 1 to test if that word shows up more than once (assuming your string is in the str variable).
Hello, here it is:
var temp = "but boys run fast boys are strong";
var count = (temp.match(/boys/g) || []).length;
console.log(count);
I hope I have helped!
You can count the occurrence of each matched word
var str = "but boys run fast boys are strong";
var matches = str.split(/\s/);
var res = matches.map(function(match) {
return str.match(new RegExp(match, "g")).length;
});
var bool = res.some(function(len) {return len > 1}));
console.log(bool);
for (var i = 0; i < matches.length; i++) {
if (res[i] > 1) console.log(matches[i], i);
}
Hello!
Here's an example with the exact answer you need:
var keyword = "boys"
var temp = "but boys run fast boys are strong";
var regex = new RegExp(keyword, "g");
var count = (temp.match(regex) || []).length;
if (count > 0) {
console.log("true, since there are " + count + " repeats of the word '" + keyword + "'");
} else {
console.log("false, not found.");
}
I hope I have helped!

Categories

Resources