I am trying to write a REGEX to test for a PANGRAM. I can do it the traditional way, but cannot seem to solve it for more than 90% of my tests with a regular expression.
Input: string
Output: true || false
function isPangram(string){
return ___________________.test(string)
}
Test Results so far.
6/10
/([a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z, \s]+)/i
6/10
/[a-z]{1}/i
6/10 /[a-z]/i
6/10 /[a-z]+/i
9/10 /a?b?c?d?e?f?g?h?i?j?k?l?m?n?o?p?q?r?s?t?u?v?w?x?y?z/i
only failed against abcdefghijklmopqrstuvwxyz
6/10 /[\w.]+/
Any help or advice is greatly appreciated.
Convert the string to lowercase
Use regex to extract all the unique alphabets from string
Check if the no of unique alphabets are 26
Code:
function isPangram(string) {
var regex = /([a-z])(?!.*\1)/g;
return (string.match(regex) || []).length === 26;
}
Regex101
var regex = /([a-z])(?!.*\1)/g;
function check() {
var val = document.getElementById('text').value.toLowerCase();
alert(val.match(regex).length == 26);
}
<input type="text" id="text" />
<button onclick="check()">Check</button>
If you are looking for Non-RegExp Solution
const isPangram = (string) =>
new Set(string.toLowerCase().match(/[a-z]/g)).size === 26;
console.log(isPangram("abcdefghijklmnopqrstuvwxyz")); //true
console.log(isPangram("The Quick Brown Fox Jumps over the lazy dog")); //true
This would be a correct answer for the challenge:
function isPangram(string){
return /(?=.*a)(?=.*b)(?=.*c)(?=.*d)(?=.*e)(?=.*f)(?=.*g)(?=.*h)(?=.*i)(?=.*j)(?=.*k)(?=.*l)(?=.*m)(?=.*n)(?=.*o)(?=.*p)(?=.*q)(?=.*r)(?=.*s)(?=.*t)(?=.*u)(?=.*v)(?=.*w)(?=.*x)(?=.*y)(?=.*z)./i.test(string)
}
It uses lookaheads with every letter to check that they are somewhere in the passed string.
using for..of loop & includes:
function isPangram(sentence) {
let lowerCased = sentence.toLowerCase();
for (let char of 'abcdefghijklmnopqrstuvwxyz') {
if (!lowerCased.includes(char)) {
return false;
}
}
return true;
}
As a single regex:
/(?:(?=(.*?([a-z]))(?!.*\2))\1){26}/i
Test it at regex101.
A breakdown:
/
(?: // non-capturing group
(?= // look-ahead assertion
(.*?([a-z])) // match a letter, preceded by as few characters as possible
(?!.*\2) // assert that this letter does not appear in the remainder of the string
// (in aggregate, this will find the last occurrence of each letter)
)\1 // actually match that group (this is done to prevent backtracking)
){26} // match 26 times (once for each letter)
/i // match case-insensitively
If the regex engine you're using supports atomic grouping (e.g. PCRE), this can be written somewhat more concisely:
/(?>.*?([a-z])(?!.*\1)){26}/i
regex101.
Given a string, detect whether or not it is a pangram. Return True if it is, False if not. Ignore numbers and punctuation. [adding case-insensitivity based on the regex code provided by #Tushar]
//Detect Pangram
function isPangram(string){
// character set capturing group with negative lookahead
let regex = /([a-z])(?!.*\1)/gi;
return (string.match(regex)).length === 26;
}
console.log(isPangram("The quick brown fox jumps over the lazy dog."));// true
console.log(isPangram("This is not a pangram."));// false
console.log(isPangram("Pack my box with five dozen liquor jugs."));// true
console.log(isPangram("This isn't a pangram!"));// false
console.log(isPangram("Detect Pangram"));// false
console.log(isPangram("How quickly daft jumping zebras vex."));// true
function isPangram(input) {
if(input.length < 26) {
return false;
}
const letters = 'abcdefghijklmnopqrstuvwxyz';
return Array.from(new Set(input.toLowerCase().split('').filter(i => i.trim()))).sort().join('') === letters;
}
console.log(isPangram("The String is abcdefghijklumnopqrstvwxyz")); //true
const isPangram = (string) =>
new Set(string.toLowerCase().match(/[a-z]/g)).size === 26;
console.log(isPangram("This is not a pangram.")); //false
console.log(isPangram("The Quick Brown Fox Jumps over the lazy dog")); //true
function isPangram(string){
let lc = string.toLowerCase()
let alphabet = "abcdefghijklmnopqrstuvwxyz"
return alphabet.split("").filter(c => lc.indexOf(c) === -1).length === 0;
}
Lower case the string
Create a string with the alphabet
split the alphabet
Using filter() with a mini function that will verify that there are 0 false statements in it (filter it so there is 0 (zero) false statement (-1) in the new array that the filter method creates ".lengeth === 0" makes sure there is no false statement
)
Related
I have strings containing 5 letters of alphabet. I would like to match those that contain letters that are consecutive in alphabet for example:
abcde - return match
nopqrs - return match
cdefg - return match
fghij - return match
but
abcef - do not return match
abbcd - do not return match
I could write all combinations but as you can write in Regex [A-Z] I assumed there must be a better way.
A very simple alternative would be to just use String.prototype.includes:
function isConsecutive(string) {
const result = 'abcdefghijklmnopqrstuvwxyz'.includes(string);
console.log(string, result);
}
// true
isConsecutive('abcde');
isConsecutive('nopqrs');
isConsecutive('cdefg');
isConsecutive('fghij');
// false
isConsecutive('abcef');
isConsecutive('abbcd');
If you can live with Python, this function converts the string sequence into numbered characters, and checks if they are consequtive (if so, they are also consecutive alphabetically):
def are_letters_consequtive(text):
nums = [ord(letter) for letter in text]
if sorted(nums) == list(range(min(nums), max(nums)+1)):
return "match"
return "no match"
print(are_letters_consequtive('abcde'))
print(are_letters_consequtive('cdefg'))
print(are_letters_consequtive('fghij'))
print(are_letters_consequtive('abcef'))
print(are_letters_consequtive('abbcd'))
print(are_letters_consequtive('noprst'))
Outputs:
match
match
match
no match
no match
no match
An alternative using javascript:
let string1 = 'abcde'
let string2 = 'fghiz'
function conletters(string) {
if(string.length > 5 || typeof string != 'string') throw '[ERROR] not string or string greater than 5'
for(let i = 0; i < string.length - 1; i++) {
if(!(string.charCodeAt(i) + 1 == string.charCodeAt(i + 1)))
return false
}
return true
}
console.log('string1 is consecutive: ' + conletters(string1))
console.log('string2 is consecutive: ' + conletters(string2))
You should definitely do it with code:
Some people, when confronted with a problem, think "I know, I'll use regular expressions." Now they have two problems.
That said, you can do better than testing all the combinations when using regexes. With lookahead expressions you can basically do "and" operation. Since you know the length you could do:
const myRegex = /(?=^(ab|bc)...$)(?=^.(ab|bc)..$)(?=^..(ab|bc).$)(?=^...(ab|bc)$)/
You will need to replace the (ab|bc) with all the possible two combinations.
For this particular case it is actually worse than testing all the possibilities (since there are only 22 possibilities) but it makes it more extensible to other situations.
I need to create regex which will look for 3 same letters in a string, regardless of the order.
Example:
'aa2ff333' -> false
'aa2a' -> true
'aaa2' -> true
I tried this but it check for consecutive letters:
(.)\1\1
Any advice?
You may use this regex with lookahead:
/([a-zA-Z])(?=(?:.*?\1){2})/
RegEx Demo
RegEx Details:
([a-zA-Z]): Match a letter [a-zA-Z] and capture it in group #1
(?=: Start lookahead
(?:.*?\1){2}: That has at least 2 occurrences of same character as in capture group #1. .*?\1 Matches back-reference \1 after 0 or more of any character. This allows matching repetitions anywhere in input.
): End lookahead
One way is to loop through the string and count occurrence of each character then check if any of character appears for exact three times
let threeChars = (str) => {
let obj = [...str].reduce((op, inp) => {
if (/[a-z]/i.test(inp)) {
op[inp] = op[inp] || 0
op[inp] += 1
}
return op
}, {})
return Object.values(obj).some(v => v === 3)
}
console.log(threeChars('aa2ff333'))
console.log(threeChars('aa2a'))
console.log(threeChars('aaa2'))
P.S:-
You can make it case insensitive by removing the i flag
In case you need to at least 3 character you can change v == 3 to v >= 3
So I am working on a project and as the title states, I am trying to find if the first letter of a string in javascript is a vowel. So far I have code that looks like this.
function startsWithVowel(word){
var vowels = ("aeiouAEIOU");
return word.startswith(vowels);
}
You're quite close, just slice the word using [0] and check that way:
function startsWithVowel(word){
var vowels = ("aeiouAEIOU");
return vowels.indexOf(word[0]) !== -1;
}
console.log("apple ".concat(startsWithVowel("apple") ? "starts with a vowel" : "does not start with a vowel"));
console.log("banana ".concat(startsWithVowel("banana") ? "starts with a vowel" : "does not start with a vowel"));
This works if you don't care about accent marks:
const is_vowel = chr => (/[aeiou]/i).test(chr);
is_vowel('e');
//=> true
is_vowel('x');
//=> false
But it will fail with accent marks commonly found in French for example:
is_vowel('é'); //=> false
You can use String#normalize to "split" a character: the base character followed by the accent mark.
'é'.length;
//=> 1
'é'.normalize('NFD').length;
//=> 2
'é'.normalize('NFD').split('');
//=> ["e", "́"] (the letter e followed by an accent)
Now you can get rid of the accent mark:
const is_vowel = chr => (/[aeiou]/i).test(chr.normalize('NFD').split('')[0]);
is_vowel('é');
//=> true
Credit to this fantastic answer to this question
startsWith only accepts a single character. For this sort of functionality, use a regular expression instead. Take the first character from the word (word[0]), and see whether its character is included in a case-insensitive character set, [aeiou]:
function startsWithVowel(word){
return /[aeiou]/i.test(word[0]);
}
function startsWithVowel(word){
return /[aeiou]/i.test(word[0]);
}
console.log(
startsWithVowel('foo'),
startsWithVowel('oo'),
startsWithVowel('bar'),
startsWithVowel('BAR'),
startsWithVowel('AR')
);
ES6 oneliner:
const startsWithVowel = word => /[aeiou]/i.test(word[0]);
I am having trouble with trying to find the last vowel in a string in JavaScript. I found out how to find the first vowel of a string and tried to modify that code but I'm stuck. I tried to edit the var vowels and I changed the 0 to -1 and vice versa but nothing worked.
Here's my code:
function endVowel(x){
var vowels = ("aeiouAEIOU");
return vowels.indexOf(x[-1]) !== 0;
}
What am I missing or doing wrong?
Use a regular expression to match a vowel, while using negative lookahead for other vowels:
function endVowel(x){
const match = x.match(/[aeiou](?!.*[aeiou])/i);
return match ? match[0] : 'No match';
}
console.log(endVowel('foobar'));
function findLastVowel(string) {
let pattern = /[aeiouAEIOU]/gim;
let result = [...string.match(pattern)]
return result[result.length - 1]
}
console.log(findLastVowel("your string here"))
vowels.indexOf(x[-1]) attempts to look for the last character in x (in fact, x[x.length-1] or x.slice(-1) is the correct syntax), but if this doesn't happen to be a vowel, it won't work. You'd need to iterate backwards from the end to test other characters in such a scenario.
To get the last vowel's index, you can strip non-vowels from the right using a regex and return the length - 1:
const lastVowel = s => s.replace(/[^aeiou]*$/i, "").length - 1;
[
"foobar",
"cdgh",
"abb",
"baabbba"
].forEach(e => console.log(`"${e}" => ${lastVowel(e)}`));
If you only want the last vowel (this is less useful than having the index, which essentially gives you both), pattern match on vowels and return the last element:
const lastVowel = s => (s.match(/[aeiou]/ig) || "").slice(-1);
[
"foobar",
"cdgh",
"abb",
"beobbba"
].forEach(e => console.log(`"${e}" => "${lastVowel(e)}"`));
One technique would be to build your endVowel atop your startVowel, using a string-reversing helper function. That's often a general principle when working with first and last values on something you can easily reverse.
Here's an example (and note that the endVowel does not in any way depend on the implementation of startVowel, only on its behavior):
const startVowel = (str, vowels = "aeiouAEIOU") =>
str .split('') .find (c => vowels .includes (c))
const reverseString = (str) => str .split('') .reverse () .join('')
const endVowel = (str) => startVowel (reverseString (str))
console .log (
startVowel ('The quick brown fox jumped over the lazy dog'), //=> 'e'
endVowel ('The quick brown fox jumped over the lazy dog'), //=> 'o'
)
Obligatory pun for English speakers: Q: What are you when you can't have a vowel movement? A: consonated.
This will give you the last vowel character in the string (keeping the original upper/lower case). If you're just looking for the index of the last vowel, that is indexOfLastVowel, of course!
function endVowel(x){
var y = x.toLowerCase();
var indexOfLastVowel = Math.max(y.lastIndexOf("a"), y.lastIndexOf("e"), y.lastIndexOf("i"), y.lastIndexOf("o"), y.lastIndexOf("u"));
return x.charAt(indexOfLastVowel);
}
console.log(endVowel("Find the last vowel in this string..."));
If no vowel is in the string, an empty string ("") is returned.
This is the simplest solution I found. It returns true or false for the given string if it contains all letters of the alphabet in it or not.
Here is the code I found:
new Set("A quick brown fox jumps over the lazy dog"
.toLowerCase()
.replace(/[^a-z]/gi, "")
.split("")
).size === 26
Any other simpler form of checking to see if a string contains all of the letters in the alphabet would be helpful.
Thanks!
You don't need to split
As it would turn out, you don't need to run String#split before passing your string to new Set. The constructor for Set, when passed a string, will, essentially, split it into single characters for you before creating the set.
Example:
new Set('A quick brown fox jumps over the lazy dog'
.toLowerCase()
.replace(/[^a-z]/g, '')
).size === 26
This works just as well because something like new Set('test') turns into
Set(3) {"t", "e", "s"}
On a side note, you can see that I've removed the i flag from the regular expression as pointed out by one of the other answers as it is unnecessary due to the .toLowerCase()
You can avoid the regex and also return early from the function once you have all the letters with something like this. It creates a set of all the letters and removes them as you find them. Once the set is empty you can return. If the loop finishes, you didn't remove everything. This only requires space for the alphabet set and since set operations are constant time, this is O(n) in the worst case.
function allLetters(str){
let alpha = new Set("abcdefghijklmnopqrstuvwxyz")
for (let c of str.toLowerCase()){
alpha.delete(c)
if (alpha.size == 0) return true
}
return false
}
let text = "hello my name if gunther"
let text2 = "The quick brown fox jumps over the lazy dog"
console.log(allLetters(text))
console.log(allLetters(text2))
This is the simplest code I found, It returns true or false for the given string mentioning the string contains all the alphabet in it or not.
new Set("<your_string>".toLowerCase().replace(/[^a-z]/g, "") ).size === 26
Example:
new Set("A quick brown fox jumps over the lazy dog".toLowerCase().replace(/[^a-z]/g, "") ).size === 26
Any other simplest form of code can be helpful. Please share it.
Thanks!
I believe this is the "simplest" w.r.t. computational complexity, requiring O(1) space (to store the character frequency table, assuming a fixed upper-bound possible input alphabet) and O(n) time as it iterates over the input string only once (plus a constant-time for the final check over the alphabet string).
var inputString = "Jaded zombies acted quaintly but kept driving their oxen forward";
var charCounts = {};
for( var i = 0; i < inputString.length; i++ ) {
var c = inputString.at( i ).toLower();
if( charCounts[c] ) charCounts[c]++;
else charCounts[c] = 1;
}
var alphabet = "abcdefghijklmnopqrstuvwyz";
for( var a = 0; a < alphabet.length; a++ ) {
if( !charCounts[ alphabet.at(a) ] ) {
console.log( "char %s does not appear in input string.", alphabet.at(a) );
}
}
As I look at it again, I can provide one tiny improvement to your code:
new Set("<your_string>".toLowerCase().replace(/[^a-z]/g, "").split("")).size === 26 .
Remove the 'i' flag on the regex because it's lowercased.
Here is a different way to due it using String.fromCharCode() and every()
const allLetters = (str) => Array.from({length:26}).map((x,i) => String.fromCharCode(i+97)).every(a => str.toLowerCase().includes(a));
console.log(allLetters("abcdefghijklmnopqrstuvwxyz"));
Or you can hardcode all the alphabets.
const allLetters = (str) => [..."abcdefghijklmnopqrstuvwxyz"].every(x => str.toLowerCase().includes(x));
console.log(allLetters('abcdefghijklmnopqrstuvwxyz'))
console.log(allLetters('abcdefghijklmnopqyz'))
function isPangram(sentence){
let lowerCased = sentence.toLowerCase();
for(let char of 'abcdefghijklmnopqrstuvwxyz'){
if(!lowerCased.includes(char)){
return false
}
}
return true
}
Here is another way using a for...of loop.