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.
Related
I was tasked with the following:
take a string
print each of the vowels on a new line (in order) then...
print each of the consonants on a new line (in order)
The problem I found was with the regex. I originally used...
/[aeiouAEIOU\s]/g
But this would return 0 with a vowel and -1 with a consonant (so everything happened in reverse).
I really struggled to understand why and couldn't for the life of me find the answer. In the end it was simple enough to just invert the string but I want to know why this is happening the way it is. Can anyone help?
let i;
let vowels = /[^aeiouAEIOU\s]/g;
let array = [];
function vowelsAndConsonants(s) {
for(i=0;i<s.length;i++){
//if char is vowel then push to array
if(s[i].search(vowels)){
array.push(s[i]);
}
}
for(i=0;i<s.length;i++){
//if char is cons then push to array
if(!s[i].search(vowels)){
array.push(s[i]);
}
}
for(i=0;i<s.length;i++){
console.log(array[i]);
}
}
vowelsAndConsonants("javascript");
if(vowels.test(s[i])){ which will return true or false if it matches, or
if(s[i].search(vowels) !== -1){ and if(s[i].search(vowels) === -1){
is what you want if you want to fix your code.
-1 is not falsey so your if statement will not function correctly. -1 is what search returns if it doesn't find a match. It has to do this because search() returns the index position of the match, and the index could be anywhere from 0 to Infinity, so only negative numbers are available to indicate non-existent index:
MDN search() reference
Below is a RegEx that matches vowel OR any letter OR other, effectively separating out vowel, consonant, everything else into 3 capture groups. This makes it so you don't need to test character by character and separate them out manually.
Then iterates and pushes them into their respective arrays with a for-of loop.
const consonants = [], vowels = [], other = [];
const str = ";bat cat set rat. let ut cut mut,";
for(const [,cons,vow,etc] of str.matchAll(/([aeiouAEIOU])|([a-zA-Z])|(.)/g))
cons&&consonants.push(cons) || vow&&vowels.push(vow) || typeof etc === 'string'&&other.push(etc)
console.log(
consonants.join('') + '\n' + vowels.join('') + '\n' + other.join('')
)
There are a couple of inbuilt functions available:
let some_string = 'Mary had a little lamb';
let vowels = [...some_string.match(/[aeiouAEIOU\s]/g)];
let consonents = [...some_string.match(/[^aeiouAEIOU\s]/g)];
console.log(vowels);
console.log(consonents);
I think that you don't understand correctly how your regular expression works. In the brackets you have only defined a set of characters you want to match /[^aeiouAEIOU\s]/g and further by using the caret [^]as first in your group, you say that you want it to match everything but the characters in the carets. Sadly you don't provide an example of input and expected output, so I am only guessing, but I thing you could do the following:
let s = "avndexleops";
let keep_vowels = s.replace(/[^aeiouAEIOU\s]/g, '');
console.log(keep_vowels);
let keep_consonants = s.replace(/[aeiouAEIOU\s]/g, '');
console.log(keep_consonants);
Please provide example of expected input and output.
You used:
/[^aeiouAEIOU\s]/g
Instead of:
/[aeiouAEIOU\s]/g
^ means "not", so your REGEX /[^aeiouAEIOU\s]/g counts all the consonants.
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]);
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.
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
)
How would you go around to collect the first letter of each word in a string, as in to receive an abbreviation?
Input: "Java Script Object Notation"
Output: "JSON"
I think what you're looking for is the acronym of a supplied string.
var str = "Java Script Object Notation";
var matches = str.match(/\b(\w)/g); // ['J','S','O','N']
var acronym = matches.join(''); // JSON
console.log(acronym)
Note: this will fail for hyphenated/apostrophe'd words Help-me I'm Dieing will be HmImD. If that's not what you want, the split on space, grab first letter approach might be what you want.
Here's a quick example of that:
let str = "Java Script Object Notation";
let acronym = str.split(/\s/).reduce((response,word)=> response+=word.slice(0,1),'')
console.log(acronym);
I think you can do this with
'Aa Bb'.match(/\b\w/g).join('')
Explanation: Obtain all /g the alphanumeric characters \w that occur after a non-alphanumeric character (i.e: after a word boundary \b), put them on an array with .match() and join everything in a single string .join('')
Depending on what you want to do you can also consider simply selecting all the uppercase characters:
'JavaScript Object Notation'.match(/[A-Z]/g).join('')
Easiest way without regex
var abbr = "Java Script Object Notation".split(' ').map(function(item){return item[0]}).join('');
This is made very simple with ES6
string.split(' ').map(i => i.charAt(0)) //Inherit case of each letter
string.split(' ').map(i => i.charAt(0)).toUpperCase() //Uppercase each letter
string.split(' ').map(i => i.charAt(0)).toLowerCase() //lowercase each letter
This ONLY works with spaces or whatever is defined in the .split(' ') method
ie, .split(', ') .split('; '), etc.
string.split(' ') .map(i => i.charAt(0)) .toString() .toUpperCase().split(',')
To add to the great examples, you could do it like this in ES6
const x = "Java Script Object Notation".split(' ').map(x => x[0]).join('');
console.log(x); // JSON
and this works too but please ignore it, I went a bit nuts here :-)
const [j,s,o,n] = "Java Script Object Notation".split(' ').map(x => x[0]);
console.log(`${j}${s}${o}${n}`);
#BotNet flaw:
i think i solved it after excruciating 3 days of regular expressions tutorials:
==> I'm a an animal
(used to catch m of I'm) because of the word boundary, it seems to work for me that way.
/(\s|^)([a-z])/gi
Try -
var text = '';
var arr = "Java Script Object Notation".split(' ');
for(i=0;i<arr.length;i++) {
text += arr[i].substr(0,1)
}
alert(text);
Demo - http://jsfiddle.net/r2maQ/
Using map (from functional programming)
'use strict';
function acronym(words)
{
if (!words) { return ''; }
var first_letter = function(x){ if (x) { return x[0]; } else { return ''; }};
return words.split(' ').map(first_letter).join('');
}
Alternative 1:
you can also use this regex to return an array of the first letter of every word
/(?<=(\s|^))[a-z]/gi
(?<=(\s|^)) is called positive lookbehind which make sure the element in our search pattern is preceded by (\s|^).
so, for your case:
// in case the input is lowercase & there's a word with apostrophe
const toAbbr = (str) => {
return str.match(/(?<=(\s|^))[a-z]/gi)
.join('')
.toUpperCase();
};
toAbbr("java script object notation"); //result JSON
(by the way, there are also negative lookbehind, positive lookahead, negative lookahead, if you want to learn more)
Alternative 2:
match all the words and use replace() method to replace them with the first letter of each word and ignore the space (the method will not mutate your original string)
// in case the input is lowercase & there's a word with apostrophe
const toAbbr = (str) => {
return str.replace(/(\S+)(\s*)/gi, (match, p1, p2) => p1[0].toUpperCase());
};
toAbbr("java script object notation"); //result JSON
// word = not space = \S+ = p1 (p1 is the first pattern)
// space = \s* = p2 (p2 is the second pattern)
It's important to trim the word before splitting it, otherwise, we'd lose some letters.
const getWordInitials = (word: string): string => {
const bits = word.trim().split(' ');
return bits
.map((bit) => bit.charAt(0))
.join('')
.toUpperCase();
};
$ getWordInitials("Java Script Object Notation")
$ "JSON"
How about this:
var str = "", abbr = "";
str = "Java Script Object Notation";
str = str.split(' ');
for (i = 0; i < str.length; i++) {
abbr += str[i].substr(0,1);
}
alert(abbr);
Working Example.
If you came here looking for how to do this that supports non-BMP characters that use surrogate pairs:
initials = str.split(' ')
.map(s => String.fromCodePoint(s.codePointAt(0) || '').toUpperCase())
.join('');
Works in all modern browsers with no polyfills (not IE though)
Getting first letter of any Unicode word in JavaScript is now easy with the ECMAScript 2018 standard:
/(?<!\p{L}\p{M}*)\p{L}/gu
This regex finds any Unicode letter (see the last \p{L}) that is not preceded with any other letter that can optionally have diacritic symbols (see the (?<!\p{L}\p{M}*) negative lookbehind where \p{M}* matches 0 or more diacritic chars). Note that u flag is compulsory here for the Unicode property classes (like \p{L}) to work correctly.
To emulate a fully Unicode-aware \b, you'd need to add a digit matching pattern and connector punctuation:
/(?<!\p{L}\p{M}*|[\p{N}\p{Pc}])\p{L}/gu
It works in Chrome, Firefox (since June 30, 2020), Node.js, and the majority of other environments (see the compatibility matrix here), for any natural language including Arabic.
Quick test:
const regex = /(?<!\p{L}\p{M}*)\p{L}/gu;
const string = "Żerard Łyżwiński";
// Extracting
console.log(string.match(regex)); // => [ "Ż", "Ł" ]
// Extracting and concatenating into string
console.log(string.match(regex).join("")) // => ŻŁ
// Removing
console.log(string.replace(regex, "")) // => erard yżwiński
// Enclosing (wrapping) with a tag
console.log(string.replace(regex, "<span>$&</span>")) // => <span>Ż</span>erard <span>Ł</span>yżwiński
console.log("_Łukasz 1Żukowski".match(/(?<!\p{L}\p{M}*|[\p{N}\p{Pc}])\p{L}/gu)); // => null
In ES6:
function getFirstCharacters(str) {
let result = [];
str.split(' ').map(word => word.charAt(0) != '' ? result.push(word.charAt(0)) : '');
return result;
}
const str1 = "Hello4 World65 123 !!";
const str2 = "123and 456 and 78-1";
const str3 = " Hello World !!";
console.log(getFirstCharacters(str1));
console.log(getFirstCharacters(str2));
console.log(getFirstCharacters(str3));
Output:
[ 'H', 'W', '1', '!' ]
[ '1', '4', 'a', '7' ]
[ 'H', 'W', '!' ]
This should do it.
var s = "Java Script Object Notation",
a = s.split(' '),
l = a.length,
i = 0,
n = "";
for (; i < l; ++i)
{
n += a[i].charAt(0);
}
console.log(n);
The regular expression versions for JavaScript is not compatible with Unicode on older than ECMAScript 6, so for those who want to support characters such as "å" will need to rely on non-regex versions of scripts.
Event when on version 6, you need to indicate Unicode with \u.
More details: https://mathiasbynens.be/notes/es6-unicode-regex
Yet another option using reduce function:
var value = "Java Script Object Notation";
var result = value.split(' ').reduce(function(previous, current){
return {v : previous.v + current[0]};
},{v:""});
$("#output").text(result.v);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<pre id="output"/>
This is similar to others, but (IMHO) a tad easier to read:
const getAcronym = title =>
title.split(' ')
.map(word => word[0])
.join('');
ES6 reduce way:
const initials = inputStr.split(' ').reduce((result, currentWord) =>
result + currentWord.charAt(0).toUpperCase(), '');
alert(initials);
Try This Function
const createUserName = function (name) {
const username = name
.toLowerCase()
.split(' ')
.map((elem) => elem[0])
.join('');
return username;
};
console.log(createUserName('Anisul Haque Bhuiyan'));