Find how many times a char is repeated in a string and remove those repeated chars by a dynamic number - javascript

I would like to write a function that recieves two parameters: String and Number.
The function will return another string that is similar to the input string, but with certain characters
removed.
The function will remove characters from consecutive runs of the same
character, where the length of the run is greater than the input parameter.
for example:
"aaab", 2 => "aab"
"aabb", 1 => "ab"
"aabbaa", 1 => "aba"
What I did:
function doSomething(string,number) {
let repeatCount = 0
debugger;
for (let i = 0; i < string.length; i++) {
if(string[i] == string[i+1]){
repeatCount++
}
if(repeatCount > number ){
string.replace(string[i],'')
}
}
console.log(string)
}
doSomething('aaab',2)
The console.log(string) prints 'aaab' but I want it to print 'aab' because the number is 2 and the char 'a' is repeated 3 times.
If there is another better way to do it , I will be happy to learn.

If there is another better way to do it, I will be happy to learn.
You could go with a .replace() approach and a regular expression with a backreference to match consecutive letters. Then you can use .slice() to remove the additional letters to get it to the defined length like so:
function shorten(string,number) {
return string.replace(/(.)\1+/g, m => m.slice(0, number))
}
console.log(shorten("aaab", 2))// => "aab"
console.log(shorten("aabb", 1))// => "ab"
console.log(shorten("aabbaa", 1))// => "aba"
The above regular expression will match any character and group it (.). This matched character is then checked for again to see if it is repeated one or more times by using \1+. The replacement function will then be invoked for each consecutive runs of letters, which you can trim down to your desired length by using .slice().
For example, take the string aabbaa. The regular expression tries to find consecutive runs of characters. The (.) would match any character, in this case, it finds "a" and puts it into a "capture group" called "1". Now the regular expression tries to find whether “a” is followed by one or more “a” characters by checking if the grouped (ie the character “a”) follows it one or more times. This is done using \1+. The first section of the aabbaa string that this regular expression matches is "aa", as we match the “a”, capture it, and find that it is repeated with \1+. When a match is found, the function m => m.slice(0, number) is ran. This function takes the match (m), which in this case is "aa", and returns the sliced version of it, giving "a". This then replaces the "aa" we matched from the original string with the value returned, thus causing "aa" to be converted to "a" (note this conversion doesn't modify the original string, it occurs in the new string that gets returned by the replace method). The /g at the end of the regular expression means repeat this for the entire string. As a result, our regular expression moves on and finds "bb". The function then gets called again but this time with m set as "bb", causing "bb" to be converted to "b". Lastly, we match "aa", this causes "aa" to get converted to "a". Once replace has finished going through the entire string, it returns the result with the returned values (as well as the part of the original string it didn’t modify) and so it gives "aba"

Not that the rest of your code is correct. But one fundamental mistake you have made is that, strings in javascript is immutable. You cannot change an element of the string like that.
string.replace(string[i],'')
This won't change 'string'. You have to make another string from it.
let str = string.replace(string[i],'')

function doSomething(string,number) {
let repeatCount = 0
debugger
let sameletter=string[0]
for (let i = 0; i < string.length;i++) {
if(string[i] == sameletter){
repeatCount++
if(repeatCount>number){
var result = string.split('')
result.splice(i, 1)
string = result.join('')
i--
}
}
else{
sameletter=string[i];
repeatCount=1;
}
}
console.log(string)
}
doSomething('aaaabbbbeeeffffgggggggggg',2)
Try this

Related

String.replace() is replacing at the wrong index even though the index is passed correctly

Here is the prompt for what I need to do. I still haven't figured out the full solution yet, and I know this is not optimal at all (I'm a complete beginner and just trying to get something working for now), but that's not what my question is.
/*
There are two types of potions:
Growing potion: "A"
Shrinking potion: "B"
If "A" immediately follows a digit, add 1 to the digit using your "addOne" function
If "B" immediately follows a digit, subtract 1 from the digit using your subtractOne
function
Create a function "usePotions" that returns a string according to these rules, removing the potions once they've been consumed.
Example:
usePotions("3A7851") ➞ "47851"
usePotions("9999B") ➞ "9998"
usePotions("9A123") ➞ "10123"
usePotions("567") ➞ "567"
*/
I am using the string.replace() method in order to both increment or decrement the digit before A or B, and remove the A or B. For the strings with "A" in them, the code is working perfectly fine. For the one with B, even though the correct index is being passed into the replace method (which is 3), it's replacing the digit at index 0. I can't for the life of me figure out why the same exact code is behaving differently. I tried manually passing different indexes and it keeps replacing the character at index 0.
const addOne = (num) => num + 1;
const subtractOne = (num) => num - 1;
const usePotions = (str) => {
let newStrA;
let finalStrA;
let newStrB;
let finalStrB;
if (!str.includes("A") && !str.includes("B")) return str;
if (str.includes("A")){
newStrA = str.replace(str[str.indexOf("A")-1], addOne(Number(str[str.indexOf("A")-1])))
finalStrA = newStrA.replace("A", "");
return finalStrA;
}
if (str.includes("B")){
console.log(str.indexOf("B")-1);
newStrB = str.replace(str[str.indexOf("B")-1], subtractOne(Number(str[str.indexOf("B")-1])))
finalStrB = newStrB.replace("B", "");
return finalStrB;
}
}
console.log(usePotions("3A7851"));
console.log(usePotions("9999B"));
console.log(usePotions("9A123"));
console.log(usePotions("567"));
The problem is that in that case, you're basically doing this:
newStrB = "9999B".replace("9", "8");
That replaces the first "9" it finds, which is at the beginning.
In your question you spoke of "indexes" but you don't pass indexes to replace, you pass the string (or regular expression) to find and the string to replace. When you use a string for the first argument, only the first match (in this case, at the beginning of the string) is replaced. You used indexes to find the "9", but the one you found is identical to the first one (of course 🙂 ), so replace replaces the wrong one.
Using indexes with substring would make more sense for this operation.
You may have misunderstood what string.replace() does, and how it can solve your problem. It will replace a string (which can be a character or more) into another one, so, as soon as there is an occurence it will replace it.
That's what you are doing right now, you are replacing the character 9 (from str[str.indexOf("B")-1]) into itself minus 1, and you don't specify where you want it to be replaced, so javascript will just take the first coming 9 coming and replace it. That's why you got 8999 and not 9998.
You can solve this by using string.substr() and doing the following:
minusOne = subtractOne(Number(str[str.indexOf("B")-1])).toString()
newStrB = str.substr(0, str.indexOf("B")-1) + minusOne + str.substr(index + minusOne.length)
You could take an object for the two functions for incrementing or devrementing a value. This approach allows to usethe letter for selecting the wanted function and return a new value for replacing the letter and leading digits.
For replacement use a regular expression which groups the numerical value and the letter with an alternative of A or B. In the replacement function omit the first parameter (the match of the complete regeular expression), here denoted with an underscore, which has just a symbolic meaning of being unused. The rest of the parameters get the content of each group.
function usePotions(string) {
const fn = {
A: v => +v + 1,
B: v => v - 1
};
return string.replace(/(\d+)(A|B)/g, (_, d, letter) => fn[letter](d));
}
console.log(usePotions("3A7851"));
console.log(usePotions("9999B"));
console.log(usePotions("9A123"));
console.log(usePotions("567"));
console.log(usePotions("999A"));

Function that modifies string based on unicode number

I want to write a function that changes each character of the input string based on whether it s a letter or not. If it is a letter, the character should be set to '1' otherwise to '0'. For instance
change('abc123') returns ('111000')
change('12ab3') returns ('00110')
My try:
function change(para){
let newstring=para;
let unicodenum=String.fromCharCode();
for (i=0; i<para.length; i++){
for (j=65; j<91; j++) {
if (unicodenum[j] === para[i])
{
newstring.replace(/para[i]/g, "1")
}
else {
newstring.replace(/para[i]/g, "0")
}
}
}
return newstring
}
Right now, the string is not modified when invoked, but simply returned.
Here is a simple one liner that will do what you needed.
I have used regrex, /[0-9]/g,"0" will replace all numbers with 0 and /[a-z]/g,"1" will replace all letters from a-z with 1.
/g denotes the global flag that will replace all occurrences of 0-9 and a-z. We can also use \d instead of [0-9]
/[0-9]/g,"0" is used before /[a-z]/g,"1" because you also want numbers to be converted to "0". If we first convert letters to numbers then everything in the string will become number and all you will get is 0. So it has to be in this specific order only.
let change=(string)=>string.replace(/[0-9]/g,"0").replace(/[a-z]/g,"1")
console.log(change('abc123'))
console.log(change('12ab3'))

Empty value if regex match not found JavaScript

I'm attempting to extract any text characters at the beginning, and the following two numbers of a string. If the string starts with a number, I'd like to get an empty string value instead so the resulting array still contains 3 values.
String:
'M2.55X.45'
Code:
'M2.55X.45'.match(/(^[a-zA-Z]+)|((\.)?\d+[\/\d. ]*|\d)/g)
Expected:
["M", "2.55", ".45"]
Actual (correct):
["M", "2.55", ".45"]
String:
'2.55X.45'
Code:
'2.55X.45'.match(/(^[a-zA-Z]+)|((\.)?\d+[\/\d. ]*|\d)/g)
Expected:
["", "2.55", ".45"]
Actual:
["2.55", ".45"]
Use /^([a-zA-Z]?)(\d*(?:\.\d+)?)[a-zA-Z](\d*(?:\.\d+)?)$/.exec("2.55X.45") instead. This returns an array where the 1st element is the entire match, so you must access groups 1-indexed, for example, match[1] for the 1st value. You can try this out here.
Your current regex uses an alternate clause (|), which creates different types of grouping depending on which alternate is matched.
Here's an example (cleaned up a bit) that creates explicit groups and makes the individual groups optional.
const regex = /^([a-zA-Z]*)?(\d*(?:\.\d+)?)([a-zA-Z]+)(\d*(?:\.\d+)?)$/
console.log(regex.exec("2.55X.45"))
console.log(regex.exec("M2.55X.45"))
Note that I've removed the g flag, so the regex's state isn't preserved.
I've also used exec instead of match to not discard capture groups.
You can try this pattern
(\D*)(\d+(?:\.\d+))\D+(\.\d+)
let finder = (str) => {
return (str.match(/^(\D*)(\d+(?:\.\d+))\D+(\.\d+)/) || []).slice(1)
}
console.log(finder('M2.55X.45'))
console.log(finder("2.55X.45"))

javascript search string for sequential periods [duplicate]

This question already has answers here:
Regex to check consecutive occurrence of period symbol in username
(2 answers)
Closed 4 years ago.
How do I search a string for multiple periods in a row, for example "...".
string.search(/.../);
string.search("[...]");
The above will return true even if a single period is detected.
string.search("...");
This is even worse as it always returns true, even if there is no period.
var banlist = ["item1", "item2", "[...]"];
I am searching each string in the list, and I need to be able to search for "...".
Edit:
Now I have a new problem. three periods exactly "..." comes out to unicode character 8230, and it is not found in the search. Any other sequence of periods can be found except for three "...".
You need to escape the . character using a backslash \
let str = "som.e ra...ndom s.tring..";
console.log(str.match(/\.{2,}/g));
where {2,} indicates that there has to be at least 2 . characters in a row and /g indicates that the whole string should be evaluated. Notice that the . is escaped.
Use \\ to escape the dot in your search.
For example '\\.\\.' or '\\.{2}'
string.search() is useful for getting the index of the first occurrence of a pattern. It returns -1 if pattern isn't found.
string.search('\\.{2}')
With string.search going through the list would be something like
for (let i = 0; i < banlist.length; i++) {
if (banlist[i].search('\\.{2}') != -1) {
// Do something
}
}
If you don't need the index and just want to know if there are 2 or more dots in the string, string.match might be useful since it returns null when the pattern doesn't match.
string.match('\\.{2}')
With string.match going through the list would be something like
for (let i = 0; i < banlist.length; i++) {
if (banlist[i].match('\\.{2}')) {
// Do something
}
}

javascript regex capturing parentheses

I don't really get the concept on capturing parentheses when dealing with javascript regex. I don't understand why we need parentheses for the following example
var x = "{xxx} blah blah blah {yyy} and {111}";
x.replace( /{([^{}]*)}/g ,
function(match,content) {
console.log(match,content);
return "whatever";
});
//it will print
{xxx} xxx
{yyy} yyy
{111} 111
so when i drop the parentheses from my pattern x the results give a different value
x.replace( /{[^{}]*}/g ,
function(match,content) {
console.log(match,content);
return "whatever";
});
//it will print
{xxx} 0
{yyy} 37
{111} 49
so the content values now become numeric value which i have no idea why. Can someone explains what's going on behind the scene ?
According to the MDN documentation, the parameters to the function will be, in order:
The matched substring.
Any groups that are defined, if there are any.
The index in the original string where the match was found.
The original string.
So in the first example, content will be the string which was captured in group 1. But when you remove the group in the second example, content is actually the index where the match was found.
This is useful with replacement of texts.
For example, I have this string "one two three four" that I want to reverse like "four three two one". To achieve that I will use this line of code:
var reversed = "one two three four".replace(/(one) (two) (three) (four)/, "$4 $3 $2 $1");
Note how $n represents each word in the string.
Another example: I have the same string "one two three four" and I want to print each word twice:
var eachWordTwice = "one two three four".replace(/(one) (two) (three) (four)/, "$1 $1 $2 $2 $3 $3 $4 $4");
The numbers:
The offset of the matched substring within the total string being
examined. (For example, if the total string was "abcd", and the
matched substring was "bc", then this argument will be 1.)
Source:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace
"Specifying a function as a parameter" section
Parenthesis are used to capture/replace only a portion of the match. For instance, when I use it to match phone numbers that may or may not have extensions. This function matches the whole string (if the if is correct), so the entire string is replaced, but I am only using a specific types of characters in a specific order, with whitespace or other("() -x") characters allowed in the input.
It will always output a string formatted to (651) 258-9631 x1234 if given 6512589631x1234 or 1 651 258 9631 1234. It also doesn't allow (or in this case format) toll-free numbers as they aren't allowed in my field.
function phoneNumber(v) {
// take in a string, return a formatted string (651) 651-6511 x1234
if (v.search(/^[1]{0,1}[-(\s.]{0,1}(?!800|888|877|866|855|900)([2-9][0-9]{2})[-)\s.]{0,2}([2-9][0-9]{2})[-.\s]{0,2}([0-9]{4})[\s]*[x]{0,1}([0-9]{1,5}){1}$/gi) !== -1) {return v.replace(/^[1]{0,1}[-(\s.]{0,1}(?!800|888|877|866|855|900)([2-9][0-9]{2})[-)\s.]{0,2}([2-9][0-9]{2})[-.\s]{0,2}([0-9]{4})[\s]*[x]{0,1}([0-9]{1,5}){1}$/gi,"($1) $2-$3 x$4"); }
if (v.search(/^[1]{0,1}[-(\s.]{0,1}(?!800|888|877|866|855|900)([2-9][0-9]{2})[-)\s.]{0,1}([2-9][0-9]{2})[-.\s]{0,2}([0-9]{4})$/gi) !== -1) { return v.replace(/^[1]{0,1}[-(\s.]{0,1}(?!800|888|877|866|855|900)([2-9][0-9]{2})[-)\s.]{0,1}([2-9][0-9]{2})[-.\s]{0,2}([0-9]{4})$/gi,"($1) $2-$3"); }
return v;
}
What this allows me to do is gather the area code, prefix, line number, and an optional extension, and format it the way I need it (for users who can't follow directions, for instance).
So it you input 6516516511x1234 or "(651) 651-6511 x1234", it will match one regex or another in this example.
Now what is happening in your code is as #amine-hajyoussef said - The index of the start of each match is being returned. Your use of that code would be better serviced by match for example one (text returned), or search for the index, as in example two. p.s.w.g's answer expands.

Categories

Resources