I'm studying regular expressions in Javascript
I've seen many ways to do exclusive matching through the OR operator with [] and | within groups ().
I can't understand how to achieve the AND behavior with regular expressions. I've done some research but I didn't find what I need.
Here an example. I have the following string: kata. I want to compare it with another string: steak. The goal is to return true if all the letters in steak is contained in kata. If I use this regular expression [steak] it returns true but actually it should return false because in kata there is no "s".
Example 2. String1 = scriptsjava, string2 = javascript, result = true (because string2 is contained in string1)
Example 3. String1 = jscripts, string2 = javascript, result = false (because string2 is not fully contained in string1)
Example 4. String1 = rkqodlw, string2 = world, result = true (because the string world is in the first string)
I thought that using regular expressions is the best way and I considered string2 as a pattern. My solution to this problem is the following
var validate=true;
var counter = 0;
str2.split("").map(val => {
counter++;
var char = new RegExp(val);
if (char.test(str1) === false) { validate = false;} else
{
str1 = str1.slice(0, counter+1) + str1.slice(counter+1,str1.length);
console.log(str1);
}
});
return validate;
I think is not the most efficient though. Do you have a better solution for this?
You want a set comparison, specifically a superset check. Simple with a Set:
let s1 = 'javascript';
let s2 = 'scriptsjava';
Set.prototype.isSuperset = function(subset) {
for (var elem of subset) {
if (!this.has(elem)) {
return false;
}
}
return true;
}
console.log(new Set(s1).isSuperset(new Set(s2)));
Well this is my second answer, I might remove the previous answer later on, if I see this seems to be helpful for you.
Although there are some good answer being posted but yet as I can see that you want to have a hold on AND operation in regex, therefore you want a regexAND answer. Thus I am going to give you an answer that works according to your need. But if you want me to be frank, then I would say, for your requirement , regex operation is kind of the last option that I would want to go for.
Now comes the answer:
For each pair , say str1, and str2. You want to see if each character of str2 is present in str1.
thus you can make AND operation in the form of positive lookahead for each character for the entire string of str2 and see if all these exists in str1 or not.
each positie lookahead would look likhe this: (?=.*w)
when written one beside another like the regex mentioned bellow they work as AND.
For example:
str1="rkqodlw"
str2="world"
make a regex by str2 in the following way:
^(?=.*w)(?=.*o)(?=.*r)(?=.*l)(?=.*d).*$
and see if that matches with str1
like this way:
function compare(str1,str2)
{
var regexAnd=str2.split("").join(")(?=.*");
var regexStr="^"+"(?=.*"+regexAnd+").*$";
console.log(regexStr); // prints the entire and condition
var re = new RegExp(regexStr,"gm");
return re.test(str1);
}
console.log(compare("scriptsjava","javascript"));
console.log(compare("jscripts","javascript"));
console.log(compare("rkqodlw","world"));
Related
This question already has an answer here:
Find all regex matches
(1 answer)
Closed last year.
Okay, so I have this string "nesˈo:tkʰo:x", and I want to get the index of all the zero-width positions that don't occur after any instance of the character ˈ (the IPA primary stress symbol). So in this case, those expected output would be 0, 1, 2, and 3 - the indices of the letters nes that occur before the one and only instance of ˈ, plus the ˈ itself.
I'm doing this with regex for reasons I'll get into in a bit. Regex101 confirms that /(?=.*?ˈ)/ should match all 4 of those zero-width positions with JS' regex flavor... but I can't actually get JS to return them.
A simple setup might look like this:
let teststring = "nesˈo:tkʰo:x";
let re = new RegExp("(?=.*?ˈ)", "g");
while (result = re.exec(teststring)) {
console.log("Match found at "+result.index);
}
...except that this loops forever. It seems to get stuck on the first match, which I understand has something to do with how RegExp.exec is supposed to auto-increment RegExp.lastIndex for global regexes, or something. But I also can't make the regex not global, or it won't return all the matches for strings like this where more than one match is expected.
Okay, so what if I manually increment RegExp.lastIndex to prevent it from looping?
let teststring = "nesˈo:tkʰo:x";
let re = new RegExp("(?=.*?ˈ)", "g");
while (result = re.exec(teststring)) {
if (result.index == re.lastIndex) {
re.lastIndex++;
} else {
console.log("Match found at "+result.index);
}
}
Now it... prints out nothing at all. Now, to be fair, if lastIndex starts at 0 by default, and the index of the first match is 0, I half expect that to be skipped over... but why isn't it at least giving me 1, 2 and 3 as matches?
Now, I can already hear the chorus of "you don't need regex for this, just do Array(teststring.indexOf("ˈ")).keys() or something to generate [0,1,2,3]". That may work for this specific example, but the actual use case is a parser function that's supposed to be a general solution for "for this input string, replace all instances of A with B, if condition C is true, unless condition D is true". Those conditions might be something like "if A is at the end of the string" or "if A is right next to another instance of A" or "if A is between 'n' and 't'". That kind of complicated string matching problem is why the parser creates and executes regexes on the fly and why regex is getting involved, and it does work for almost everything except this one annoying edge case, which I'd rather not have to refactor the entire mechanism of the parser to deal with if I don't have to.
Use String.prototype.matchAll() to get all the matches.
let teststring = "nesˈo:tkʰo:x";
let re = new RegExp("(?=.*?ˈ)", "g");
[...teststring.matchAll(re)].forEach(result =>
console.log("Match found at " + result.index)
)
.search() returns the index of a match. .exec() returns an array of the match. Note a look ahead (?=) isn't needed, a standard capture group () suffices.
const str =`nesˈo:tkʰo:x",`;
const rgx = /(.*?ˈ)/;
let first = str.search(rgx);
let last = rgx.exec(str)[0].length - 1;
console.log('Indices: '+first+' - '+(first + last)+' \nLength: '+(last+1));
I am trying to search a single whole word through a textbox. Say I search "me", I should find all occurrences of the word "me" in the text, but not "memmm" per say.
I am using JavaScript's search('my regex expression') to perform the current search (with no success).
After several proposals to use the \b switches (which don't seem to work) I am posting a revised explanation of my problem:
For some reason this doesn't seem to do the trick. Assume the following JavaScript search text:
var lookup = '\n\n\n\n\n\n2 PC Games \n\n\n\n';
lookup = lookup.trim() ;
alert(lookup );
var tttt = 'tttt';
alert((/\b(lookup)\b/g).test(2));
Moving lines is essential
To use a dynamic regular expression see my updated code:
new RegExp("\\b" + lookup + "\\b").test(textbox.value)
Your specific example is backwards:
alert((/\b(2)\b/g).test(lookup));
Regexpal
Regex Object
Use the word boundary assertion \b:
/\bme\b/
You may use the following code:
var stringTosearch ="test ,string, test"; //true
var stringTosearch ="test string test"; //true
var stringTosearch ="test stringtest"; //false
var stringTosearch ="teststring test"; //false
if (new RegExp("\\b"+"string"+"\\b").test(stringTosearch)) {
console.log('string found');
return true;
} else {
return false;
}
<script type='text/javascript'>
var lookup = '\n\n\n\n\n\n2 PC Games \n\n\n\n';
lookup = lookup.trim() ;
alert(lookup );
var tttt = 'tttt';
alert((/\b(lookup)\b/g).test(2));
</script>
It's a bit hard to tell what you're trying to do here. What is the tttt variable supposed to do?
Which string are you trying to search in? Are you trying to look for 2 within the string lookup? Then you would want:
/\b2\b/.test(lookup)
The following, from your regular expression, constructs a regular expression that consists of a word boundary, followed by the string "lookup" (not the value contained in the variable lookup), followed by a word boundary. It then tries to match this regular expression against the string "2", obtained by converting the number 2 to a string:
(/\b(lookup)\b/g).test(2)
For instance, the following returns true:
(/\b(lookup)\b/g).test("something to lookup somewhere")
I am trying to enter 'username' in a webpage using VBA. So in the source code of the webpage, there are some modifications done to the 'username' value.
I have attached the code,
function myFunction()
{
document.str.value = "Abc02023";
document.str.value = document.str.value.toUpperCase();
pattern = new RegExp("\\*", "g");
document.str.value = document.str.value.replace(pattern, "");
document.str.value = document.str.value.replace(/^\s+/, "");
document.str.value = document.str.value.replace(/\s+$/, "");
}
I read about these and from my understanding, after the modifications document.str.value is ABC02023.
Obviously I am wrong as there would not be no point in doing all these modifications then. Also, I am getting an 'incorrect username error'.
So can anybody please help me to understand these. What would be the value of document.str.value and how did you figure it out? I am new to JavaScript so please forgive me if I am being too slow...
Looks like you are using some very old code to learn from. ☹
Let's see if we can still learn something by bringing this code up to date, then you go find some newer learning materials. Here is a well-written book series with free online versions available: You Don't Know JS.
function myFunction() {
// Assuming your code runs in a browser, `document` is equal to the
// global object. So if in a browser and somewhere outside the function
// a variable `str` has been created, this will add an attribute `value`
// to `str` and set the value of `str.value` to 'Abc02023'. If there is
// no already existing object `document` (for instance not running in
// a browser) or if document does not have an already created property
// called`str` then this will throw a TypeError because you cannot add
// a property to `undefined`.
document.str.value = "Abc02023";
// You probably were just trying to create a new variable `str` so let's
// just start over
}
Second try
function myFunction() {
// create a variable `str` and set it to 'Abc02023'
var str = "Abc02023";
// Take value of str and convert it to all capital letters
// then overwrite current value of str with the result.
// So now `str === 'ABC02023'
str = str.toUpperCase();
// Create a regular expression representing all occurences of `*`
// and assign it to variable `pattern`.
var pattern = new RegExp("\\*", "g");
// Remove all instances of our pattern from the string. (which does not
// affect this string, but prevents user inputting some types of bad
// strings to hack your website.
str = str.replace(pattern, "");
// Remove any leading whitespace form our string (which does not
// affect this string, but cleans up strings input by a user).
str = str.replace(/^\s+/, "");
// Remove any trailing whitespace form our string (which does not
// affect this string, but cleans up strings input by a user).
str = str.replace(/\s+$/, "");
// Let's at least see our result behind the scenes. Press F12
// to see the developer console in most browsers.
console.log("`str` is equal to: ", str );
}
Third try, let's clean this up a little:
// The reason to use functions is so we can contain the logic
// separate from the data. Let's pull extract our data (the string value)
// and then pass it in as a function parameter
var result = myFunction('Abc02023')
console.log('result = ', result)
function myFunction(str) {
str = str.toUpperCase();
// Nicer syntax for defining regular expression.
var pattern = /\*/g;
str = str.replace(pattern, '');
// Unnecesarry use of regular expressions. Let's use trim instead
// to clean leading and trailing whitespace at once.
str = str.trim()
// let's return our result so the rest of the program can use it
// return str
}
Last go round. We can make this much shorter and easier to read by chaining together all the modifications to str. And let's also give our function a useful name and try it out against a bad string.
var cleanString1 = toCleanedCaps('Abc02023')
var cleanString2 = toCleanedCaps(' ** test * ')
console.log('cleanString1 = ', cleanString1)
console.log('cleanString2 = ', cleanString2)
function toCleanedCaps(str) {
return str
.toUpperCase()
.replace(/\\*/g, '')
.trim()
}
#skylize answer is close
what is equivalent to your code is actually
function toCleanedCaps(str) {
return str
.toUpperCase()
.replace(/\*/g, '') // he got this wrong
.trim()
}
Let's go over the statements one by one
document.str.value = document.str.value.toUpperCase();
makes the string uppercase
pattern = new RegExp("\\*", "g");
document.str.value = document.str.value.replace(pattern, "");
replaces between zero and unlimited occurences of the \ character , so no match in this case.
document.str.value = document.str.value.replace(/^\s+/, "");
replaces any whitespace character occurring between one and unlimited times at the beginning of the string, so no match.
document.str.value = document.str.value.replace(/\s+$/, "");
replaces any whitespace character occurring between one and unlimited times at the end of the string, so no match.
You are right. With "Abc02023" as input, the output is what you suggest.
I am currently trying to figure out how to solve the above named problem.
Specifically I want to check if the string does not contain the word "stream" both in capital and lowercase letters.
Here's my code so far:
if (((gewaesser_name1.includes("Stream") == "false") ||
(gewaesser_name1.includes("stream") == "false")) &&
((gewaesser_name2.includes("Stream") == "false") ||
(gewaesser_name2.includes("stream") == "false")))
{var a= "..."}
The code does obviously not work as the results are not what I expect them to be.
I also tried to use the indexOf method before using the following syntax variations:
gewaesser_name2.indexOf("stream") == -1
gewaesser_name2.indexOf("stream") < 0
None of these variations seem to work for me. Could anyone please give me a hint what's the problem here? I used the indexOf method before many times but always when I wanted to check if a string did contain a specific word, not the other way round.
I suggest to use String+toLowerCase and check with String#indexOf, because it works in every browser.
if (gewaesser_name1.toLowerCase().indexOf("stream") === -1 && gewaesser_name2.toLowerCase().indexOf("stream") === -1) {
var a = "..."
}
indexOf() is the correct approach and the case issue can be easily resolved by forcing the test string to lower or upper case before the test using .toLowerCase() or .toUpperCase():
const lookupValue = "stream";
const testString1 = "I might contain the word StReAm and it might be capitalized any way.";
const testString2 = "I might contain the word steam and it might be capitalized any way.";
function testString(testData, lookup){
return testData.toLowerCase().indexOf(lookup) === -1;
}
function prettyPrint(yesNo){
return "The string does" + (yesNo ? " NOT" : "") + " contain \"stream\" in some form."
}
console.log(prettyPrint(testString(testString1, lookupValue)));
console.log(prettyPrint(testString(testString2, lookupValue)));
You may want to compare the returned results of your include() with strictly equal operands, === false or === true, it's much better practice however not really needed for this, just looks like you might benefit from knowing the different as comparing boolean to a string is an odd thing to do. I'd also not be checking "Stream" and "stream" try using toLowerCase() instead like so, var str1_check = gewaesser_name1.toLowerCase();
I'd check for stream using the lowercase "stream" as your new strings will all be in lower case, as well you want them to be separate from your initial variables as you may not want those names forced to lowercase. I'd use str1_check.includes("stream") to check if this string has the string "stream" in it, because this result is truthy or falsey you can perform your check like so.
if(str1_check.includes("stream")) {
//this string contained stream
}
I looks like your if logic here was if the first name doesn't contain "stream" or name 1 and 2 do not contain stream but your checking name 1 with lowercase "stream" and name 2 with uppercase "stream". it looks like you just want both names not to contain stream, this can be much more easily performed like this.
var str1_check = gewaesser_name1.toLowerCase(),
str2_check = gewaesser_name2.toLowrCase();//this way you're not making multiple toLowerCase calls in your conditional and maintain the state of your two names.
if(!str1_check.includes("stream") && !str2_check.includes("stream")){
//your code on truthey statement
}
Thanks for the fast feedback guys, the code is now running perfectly fine!
I am using the following code:
`if (gewaesser_name1.toLowerCase().indexOf("stream") === -1 && gewaesser_name2.toLowerCase().indexOf("stream") === -1)
{var a = "does NOT contain stream"}
else {var a= "does contain stream"}
`
this is a operation you can do in regex:
const testString1 = "I might contain the word StReAm and it might be capitalized any way.";
const testString2 = "I might contain the word steam and it might be capitalized any way.";
const re = /stream/i
console.log( !!(testString1.match(re) ));
console.log( !!(testString2.match(re) ))
I would prefer to use javascript RegExp like this:
function includesMatch(lookupValue, testString){
var re = new RegExp(lookupValue, 'i'); //Here the 'i' means that we are doing a case insensitive match
return testString.match(re) !== null
}
var lookup = "stream";
var test1 = "do I have a StrEAm in me?";
var test2 = "well at least I don't";
console.log(includesMatch(lookup, test1));
console.log(includesMatch(lookup, test2));
var name = "AlbERt EINstEiN";
function nameChanger(oldName) {
var finalName = oldName;
// Your code goes here!
finalName = oldName.toLowerCase();
finalName = finalName.replace(finalName.charAt(0), finalName.charAt(0).toUpperCase());
for(i = 0; i < finalName.length; i++) {
if (finalName.charAt(i) === " ")
finalName.replace(finalName.charAt(i+1), finalName.charAt(i+1).toUpperCase());
}
// Don't delete this line!
return finalName;
};
// Did your code work? The line below will tell you!
console.log(nameChanger(name));
My code as is, returns 'Albert einstein'. I'm wondering where I've gone wrong?
If I add in
console.log(finalName.charAt(i+1));
AFTER the if statement, and comment out the rest, it prints 'e', so it recognizes charAt(i+1) like it should... I just cannot get it to capitalize that first letter of the 2nd word.
There are two problems with your code sample. I'll go through them one-by-one.
Strings are immutable
This doesn't work the way you think it does:
finalName.replace(finalName.charAt(i+1), finalName.charAt(i+1).toUpperCase());
You need to change it to:
finalName = finalName.replace(finalName.charAt(i+1), finalName.charAt(i+1).toUpperCase());
In JavaScript, strings are immutable. This means that once a string is created, it can't be changed. That might sound strange since in your code, it seems like you are changing the string finalName throughout the loop with methods like replace().
But in reality, you aren't actually changing it! The replace() function takes an input string, does the replacement, and produces a new output string, since it isn't actually allowed to change the input string (immutability). So, tl;dr, if you don't capture the output of replace() by assigning it to a variable, the replaced string is lost.
Incidentally, it's okay to assign it back to the original variable name, which is why you can do finalName = finalName.replace(...).
Replace is greedy
The other problem you'll run into is when you use replace(), you'll be replacing all of the matching characters in the string, not just the ones at the position you are examining. This is because replace() is greedy - if you tell it to replace 'e' with 'E', it'll replace all of them!
What you need to do, essentially, is:
Find a space character (you've already done this)
Grab all of the string up to and including the space; this "side" of the string is good.
Convert the very next letter to uppercase, but only that letter.
Grab the rest of the string, past the letter you converted.
Put all three pieces together (beginning of string, capitalized letter, end of string).
The slice() method will do what you want:
if (finalName.charAt(i) === " ") {
// Get ONLY the letter after the space
var startLetter = finalName.slice(i+1, i+2);
// Concatenate the string up to the letter + the letter uppercased + the rest of the string
finalName = finalName.slice(0, i+1) + startLetter.toUpperCase() + finalName.slice(i+2);
}
Another option is regular expression (regex), which the other answers mentioned. This is probably a better option, since it's a lot cleaner. But, if you're learning programming for the first time, it's easier to understand this manual string work by writing the raw loops. Later you can mess with the efficient way to do it.
Working jsfiddle: http://jsfiddle.net/9dLw1Lfx/
Further reading:
Are JavaScript strings immutable? Do I need a "string builder" in JavaScript?
slice() method
You can simplify this down a lot if you pass a RegExp /pattern/flags and a function into str.replace instead of using substrings
function nameChanger(oldName) {
var lowerCase = oldName.toLowerCase(),
titleCase = lowerCase.replace(/\b./g, function ($0) {return $0.toUpperCase()});
return titleCase;
};
In this example I've applied the change to any character . after a word boundary \b, but you may want the more specific /(^| )./g
Another good answer to this question is to use RegEx to do this for you.
var re = /(\b[a-z](?!\s))/g;
var s = "fort collins, croton-on-hudson, harper's ferry, coeur d'alene, o'fallon";
s = s.replace(re, function(x){return x.toUpperCase();});
console.log(s); // "Fort Collins, Croton-On-Hudson, Harper's Ferry, Coeur D'Alene, O'Fallon"
The regular expression being used may need to be changed up slightly, but this should give you an idea of what you can do with regular expressions
Capitalize Letters with JavaScript
The problem is twofold:
1) You need to return a value for finalName.replace, as the method returns an element but doesn't alter the one on which it's predicated.
2) You're not iterating through the string values, so you're only changing the first word. Don't you want to change every word so it's in lower case capitalized?
This code would serve you better:
var name = "AlbERt EINstEiN";
function nameChanger(oldName) {
// Your code goes here!
var finalName = [];
oldName.toLowerCase().split(" ").forEach(function(word) {
newWord = word.replace(word.charAt(0), word.charAt(0).toUpperCase());
finalName.push(newWord);
});
// Don't delete this line!
return finalName.join(" ");
};
// Did your code work? The line below will tell you!
console.log(nameChanger(name));
if (finalName.charAt(i) === " ")
Shouldn't it be
if (finalName.charAt(i) == " ")
Doesn't === check if the object types are equal which should not be since one it a char and the other a string.