I have a string, msg. I want to check if the third character in the string exists or not.
I've tried if (msg.substring(3, 4) == undefined) { ... } but this doesn't work. What would the substring be equal to if it does not exist?
The third character of the string exists if the string has three characters, so the check you are looking for is
msg.length >= 3
As to your question about a non-existent substring, you get the empty string, which you can test in a console:
> "dog".substring(8, 13)
''
It's generally worth a mention that indexing and taking the length of characters is fraught with Unicode-related difficulties, because what JavaScript thinks of as a character is not really a character, but a UTF-16 character code. So watch out for:
> "π¬π€".length
4
> "π¬π€"[1]
'οΏ½'
> "π¬π€"[2]
'οΏ½'
> [..."π¬π€"][1]
'π€'
So if you are looking for better characters do:
[...msg].length >= 3
That will still not work with Unicode grapheme clusters, but it is at least better than taking the actual length of string, which is broken in JavaScript (unless you are dealing with what they call BMP characters).
From the documentation:
Any argument value that is less than 0 or greater than stringName.length is treated as if it were 0 and stringName.length, respectively.
This isn't super intuitive but your example will be an empty string "" (or if the first position has a character and you've added a larger gap, it will be a string that is shorter than the space between indexStart and indexEnd:
const str = 'hi';
console.log(str.substring(1, 4)); // is "i"
You could take a standard property accessor for getting a character or undefined.
const
string0 = '',
string1 = 'a',
string2 = 'ab',
string3 = 'abc',
string4 = 'abcd';
console.log(string0[2]); // undefined
console.log(string1[2]); // undefined
console.log(string2[2]); // undefined
console.log(string3[2]); // c
console.log(string4[2]); // c
If string doesn't contains enough letters substrings doesn't returns nothing. You can try use string as array which return undefined if index doesn't exist.
if ( msg[2] !== undefined ) {}
Related
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
I am quite new to JS and i have a problem:
I am trying to compare a string (as integer) with an input (as integer). The problem occurs because, the string is written as 10'000'000 for e.g. and the integer as 10000000, with 4 possible scenarios:
- 1'000'000
- 10'000'000
- 100'000'000
- 1'000'000'000
That is, the number (as string) can be from 1 million to 1 billion.
I want to erase (or replace with "") all of my " ' " characters so that the format which i will get for the string will be the same as the one of the integer
E.g. Integer: 95500000 String: 95'500'000 ---> need it to be 95500000
A similar solution but not quite the same is provided here:
Regex remove repeated characters from a string by javascript
String: 95'500'000 ---> need it to be 95500000
Thatβs as simple as "95'500'000".replace(/'/g, '')
g modifier makes it replace all occurrences, and not just the first one.
const str = "9'0'0000"
const removedSlashStr = str.split('').reduce((removedSlash, char) => {
if(char !== "'") removedSlash+=char
return removedSlash
}, '')
console.log(removedSlashStr) // "900000"
I have to validate Name fields in my project and have different validation conditions for it.
Starting Name:
Min. length of 2 chars.
Can be alphabetic chars, blanks and hyphens.
The 1st char must be alphabetic.
Blanks & Hyphens must not be adjacent
My Regex:-
function fname(value){
var fn = new RegExp("([a-zA-Z]{1}[a-zA-Z]*[ -]{0,1}[a-zA-Z])+([ -]{0,1}[a-zA-Z]+)*");
if(fn.test(value)){
return true;
}
else{
return false;
}
}
Last Name:
Can be alphabetic chars, blanks, hyphens and apostrophes.
1st char must be alphabetic.
Blanks, hyphens and apostrophes must not be adjacent.
1 char is acceptable only if that char is O
My Regex:
function fname(value){
var ln = new RegExp("([a-zA-Z]+([a-zA-Z]|([ '][a-zA-Z])|([-][a-zA-Z])){1,}|[O]{1})");
if(ln.test(value)){
return true;
}
else{
return false;
}
}
Both these regex are failing as they are accepting:
Alphanumeric characters are getting acceptable which shouldn't be.
space, hyphen adjacent (in case of starting name) and space, hyphen and apostrophes adjacent (in case of last name) at any position in string.
While this can be done with a single regex, the easiest solution is to just separate the tests:
function is_valid_first_name(str) {
return (
str.length >= 2 &&
/^[a-zA-Z \-]*$/.test(str) &&
/^[a-zA-Z]/.test(str) &&
!/[ \-]{2}/.test(str)
);
}
function is_valid_last_name(str) {
return (
/^[a-zA-Z \-']*$/.test(str) &&
/^[a-zA-Z]/.test(str) &&
!/[ \-']{2}/.test(str) &&
(str.length > 1 || str === 'O')
);
}
At first glance I see you aren't specifying start and end of string boundaries in your regexps so any string conatining a matching substring will validate:
> "Hello".match(/[a-z]+/)
[ 'ello', index: 1, input: 'Hello' ]
> "Hello".match(/^[a-z]+$/)
null
Another ugly thing I see are unescaped hyphens in character classes ([ -]). Even in this case is valid (it will work as you expect) but is ugly because it has special meaning in character classes (depending on context) so minimal and apparently inoffensive changes may break things that were already working:
> "-".match(/[abc-]/);
[ '-', index: 0, input: '-' ]
> "-".match(/[abc-j]/);
null
Answering your question: Ignoring 4th rule it's pretty easy:
> "hello-world as foo".match(/^[a-zA-Z][a-zA-Z\s\-]+$/)
[ 'hello-world as foo', index: 0, input: 'hello-world as foo' ]
First character class ([a-zA-Z]) ensures 2nd rule and is compatible with rules n. 1 and 3 too.
The second character class ([a-zA-Z\s\-]) matches any valid character according rule n. 2.
Following quantifier (+) ensures one or more occurrences which, plus the initial character matched by initial character class sums 2 or more characters (so fits rule n. 1).
Finally, starting and ending ^ and $ boundaries ensures, that matching starts from the really beginning of the string and ends at its end so, as I explained earlier, matching substrings aren't enough to validate if invalid characters are present.
4th rule is a bit more tricky. I think it can be approached by lookbehind and lookahead expressions, but they are not available on all regex engines (even in javascript I think they aren't at least in some ancient versions).
...and, even if available, they are always suboptimal (from the regex engine implementation point of view).
On the other hand, you could rely on grouping instead of character classes combining groups with spaces and groups with hyphens, but that will darken your final expression apart of making it harder to build, understand an test. So, in my opinion it isn't a good solution too.
BUT if you are not forced to use single regular expression it's pretty easy to check apart by an ad hoc expression.
function fname(value){
return !!( // <- (optional) Boolean cast for more consistent return type.
value.match(/^[a-zA-Z][a-zA-Z\s\-]+$/)
&& ! value.match(/\s-|-\s/)
);
}
console.log (fname("hello-world as foo")); // true
console.log (fname("hello- world as foo")); // false
console.log (fname("hello -world as foo")); // false
console.log (fname("-hello-world as foo")); // false (null without "!!" cast).
console.log (fname(" hello-world as foo")); // false (null without "!!" cast).
...as a final note, I used "\s" character class instead of "" for spaces. This matches other spacing characters too (like tabs and, in some conditions, line breaks, etc...) if you don't want to accept those characters, replace all "\s" occurrence by simple spaces.
I preferred to use "\s" in the sake of readability (and because in most cases I like it much more if other spacings are acceptable, but I think in this case it's not).
Last name rules are pretty much the same so required changes are trivial following the same reasoning.
First, both functions have the same name. Maybe that's just a typo.
In any case, I think this does what you want for the first name. It doesn't allow a trailing space or hyphen, but that seems to be implied.
const re_fname = /^[a-zA-Z](?:[- ]?|(?:[a-zA-Z][- ]?)+)$/;
function fname(value){
const res = re_fname.test(value);
console.log("%s: %s", res ? "PASS" : "FAIL", value);
return res;
}
fname("foo-bar");
fname("foobar");
fname("f-");
fname("f--");
fname("foo-bar-");
fname("foo-bar--");
fname("-foo-bar");
fname("foo--bar");
fname("foo bar");
fname("foo bar");
fname("foo- bar");
And the last name, which is nearly identical, only adding the apostrophe to the set, and allowing for a single O match.
const re_lname = /^(?:O|(?:[' -]?|[a-zA-Z](?:[' -]?[a-zA-Z])+)[' -]?)$/;
function lname(value){
const res = re_lname.test(value);
console.log("%s: %s", res ? "PASS" : "FAIL", value);
return res;
}
lname("O");
lname("X");
lname("foobar");
lname("foo-bar");
lname("foo-bar-");
lname("foo-bar-'");
lname("foo-bar'");
lname("-foo-bar");
lname("foo--bar");
lname("foo bar");
lname("foo bar");
lname("foo- bar");
lname("foo'bar");
lname("foo' bar");
lname("foo'- bar");
lname("foo-'bar");
The following example is a bit confusing to me:
var text = "A string with 3 numbers in it ... 42 and 88.";
var number = /\b(\d+)\b/g;
var match;
while (match = number.exec(text)){
console.log("Found", match[1], "at", match.index);
}
Specifically, I don't understand how this has a "looping" effect. How does it run through all the matches within one string if it keeps calling match[1]. Is there some kind of side effect with exec that I am unaware of?
Edit:
I still would like an answer to how match[1] is working.
How does match[1] produce any answer? When I test this type of thing myself, I get undefined, look
> var y = /\d+/g.exec('5')
undefined
> y
[ '5', index: 0, input: '5' ]
> y[1]
undefined
Whats going on here? Wouldn't it be y[0], or in the case above, match[0]? Like:
> y[0]
'5'
The RegExp object remembers the last matched position with lastIndex property.
Quoting MDN Documentation,
If your regular expression uses the "g" flag, you can use the exec() method multiple times to find successive matches in the same string. When you do so, the search starts at the substring of str specified by the regular expression's lastIndex property (test() will also advance the lastIndex property).
Important Note: The first part of the first line of the quoted section is important. If your regular expression uses the"g"flag. Only if the RegEx has g flag you will get this behavior.
I am using the following code:
if (store.getItem('TopicID') != "00")
TopidID is always 4 digits and what I need to do is change this to check if the last two digits are "00".
Can I do this as part of the above by just adding ".substring(from, to)" or do I need to put this into a variable and then check the variable?
Use if (!/00$/.test(store.getItem('TopicID')) to check for the last 2 digits not forming '00'. That way the length of the value of store.getItem('TopicID') doesn't matter, you always check for the last two characters of the value, and you don't need the substring 'chaining'.
By the way, I supposed store.getItem('TopicID') returns a String here.
To be complete and in response to Paul Phillips comment: in !/00$/.test([somestring]), /00$/ is a Regular Expression, a special text string for describing a search pattern. In this case it means: for the string resulting from store.getItem('TopicID'), check if you can find 2 consecutive zero's, where the $-sign means 'check for that pattern at the end of the string'.
To be even more complete on the subject of 'chaining': as long as a method is contained by the object to chain, everything can be chained. A completely nonsensical example of that:
Number(/00$/.test('0231')) //convert a boolean to a Number
.toFixed(2) //a Number has the toFixed method
.split('.')[1] //toFixed returns a String, which has method split
.substr(1) //the second element is a string, so substr applies
.concat(' world') //still a string, so concat will do
.split(' ') //string, so split works
.join(' hello ') //from split an array emerged, so join applies
;
//=> result of this chain: '0 hello world'
Can I do this as part of the above by just adding ".substring(from, to)"
Yes, you can. You got the wrong syntax, though.
if (store.getItem('TopicID').substring( 2 ) != "00")
Chaining it would work. So would extracting a local variable. So no, you don't need to. Do it if you think it makes the code more readable.
you can do using slice too
if (store.getItem('TopicID').slice(2,4) != "00") {
// Do Your Stuff
}
Try to use substr or substring with negative start:
if ( store.getItem('TopicID').substr(-2) !== "00" ){...}
or
if ( store.getItem('TopicID').substring(-2) !== "00" ){...}
if it's four digits, you can use
if (store.getItem('TopicID') % 100)
var yourString = (store.getItem('TopicID'))
if(yourString.substring((yourString.length - 2), 2) == "00")
The code above doesn't care how long your string is. It gets the last two digits and compare to "00"