Generating acronyms using JavaScript - javascript

This code below with its function to generate acronyms was extracted from Stanford Uni's lecture notes. The code checks for every character and correctly handles strings that have leading, trailing, or multiple spaces, or even hyphens. However, I have difficulty understanding a line of code.
function acronym(str) {
let result = "";
let inWord = false;
for (let i = 0; i < str.length; i++) {
let ch = str.charAt(i);
if (isLetter(ch)) {
if (!inWord) result += ch;
inWord = true;
} else {
inWord = false;
}
}
return result;
}
function isLetter(ch) {
return ch.length === 1 &&
ALPHABET.indexOf(ch.toUpperCase()) !== -1;
}
As shown in the code above, I'm not quite sure how the "inWord" variable works. I'm not sure how it sets word boundaries that are indicated by sequences of nonletters. If you don't mind, can someone please enlighten me?
Your help is much appreciated. Thanks!

The code tries to make an acronym, i.e. take the first letter of every word to create to create a new word.
Translation of the loop:
If the current character is a letter check if boolean flag is false
If the boolean is false, add the character to the current acronym value
Set the boolean flag to true, so the other letters of the word will not be executed until a separator is found
Start from step 1 when a separator is found (non-alphabetic character).
So basically it just aggregates the first letters of every word into a new string.

Related

JS Regex returning -1 & 0

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.

Finding characters of a word in a string, optimized

I am doing doing a few code challenges in the hope to learn some new stuff. Currently I have written a piece of code that find charachter of a given word in a string of random letters.
I thought regexp might be the best performance-wise (it's one of the objectives). This code passes the checks but takes too long with absurd long strings. Is there any way I can improve this? It's really ugly as is honestly. I've tried several approaches but I am probably just really a newbie at reg exp etc.
before all the if statements I only used regexp but if str2 which is the word I am looking for had double characters it would come back 'true' because it would count already counted characters. That is why I am using replace to exclude them. That's all I could get.
the goal is to return true if a portion of str1 can be rearranged to form str2, otherwise return false. Only lower case letters will be used (a-z). No punctuation or digits will be included. for example scramble('aabbcamaomsccdd','commas') should return true
function scramble (str1, str2)
{
var o = 0; // tracks amount of matched letters.
for(i = 0; i < str2.length; i++)
{
var regex1 = new RegExp (str2[i]) ; // select letter from word that needs to be found
if( regex1.test(str1) == true)// if selected character is found us replace to remove it from the random characters string for next iteration.
{
str1 = str1.replace(regex1 ,"");
o++; // increment o if character is removed from random string.
}
}
//check if amount of removed characters equals total characters of word that we want.
if ( o == str2.length)
{
return true
}
if (o !== str2.length)
{
return false
}
}
Update: I flagged the hash table as answer because afaik this was not doable with regexp it seems also I was able to achieve a proper result with .split and loops myself plus the hash table also achieved this.
if-less methodology!
i didnt stress it on tests but looks fine
function scramble(str1, str2) {
str1 = [...str1];
return [...str2].filter((str => (str == str1.splice(str1.indexOf(str), 1)))).join('') == str2;
}
You could take a hash table, count the wanted characters and return if no count is not more necessary.
function scramble (str1, str2) {
var counter = {},
keys = 0;
for (let i = 0; i < str2.length; i++) {
if (!counter[str2[i]]) {
counter[str2[i]] = 0;
keys++;
}
counter[str2[i]]++;
}
for (let i = 0; i < str1.length; i++) {
if (!counter[str1[i]]) continue;
if (!--counter[str1[i]] && !--keys) return true;
}
return false;
}
console.log(scramble('abc', 'cba'));
console.log(scramble('abc', 'aba'));
console.log(scramble('abcdea', 'aba'));
console.log(scramble('aabbcamaomsccdd', 'commas'));

How can I check a word is made of letters in different array

I am writing a word game in Vue.
You start with a string of random characters and you have to input a word that can be made from that string, eg for "ABEOHSTD" the user can enter "BASE" for a score of 4.
I have an external word list in a .txt file (which I also can't get working, but that's a separate issue) I need to verify the words against, but I'm having trouble verifying the words can be made from the given random string.
I don't know how I'd approach making sure each letter can only be used as many times as it appears in the array, or even store the scores but I just want to get this first part working.
I have tried splitting both the entered word and random string into an array of each character, looping through the array and checking if each character in the user entered string is included in the random string array.
splitUserCurrentWord = this.userAttemptedWord.split("");
for (var i = 0; i <= splitUserCurrentWord.length; i++) {
if (this.randomStringForGame.split("").includes(splitUserCurrentWord[i])) {
return true;
//i++
}
else {
return false;
}
}
}
Currently I expect to resolve to true if all the letters in the user inputted word are present in the random string array, but it seems to only resolve to true or false based on the first letter of the array, which isn't good because as long as the first letter is in the random string array it will register as true/correct.
jsfiddle of the entire thing so far:
https://jsfiddle.net/sk4f9d8w/
Your return statement is exiting the loop after the 1st iteration.
One way to do it is to use Array.every to verify all the letters and String.includes to check if the letter is part of the accepted String
const randomString = "ABEOHSTD";
console.log(isWordValid(randomString, "BASE"));
console.log(isWordValid(randomString, "BASEU"));
function isWordValid(validLetters, attemtedWord) {
const attemptedWordSplitted = attemtedWord.split("");
return attemptedWordSplitted.every(attemptedLetter => validLetters.includes(attemptedLetter));
}
If you don't allow to reuse the same letter multiple times, you need another approach by deleting the used letter from the list of acceptable letters
const randomString = "ABEOHSTD";
console.log(isWordValid(randomString, "BASE"));
console.log(isWordValid(randomString, "BAASE"));
console.log(isWordValid(randomString, "BASEU"));
function isWordValid(validLetters, attemptedWord) {
const validLettersSplitted = validLetters.split("");
const attemptedWordSplitted = attemptedWord.split("");
return attemptedWordSplitted.every(attemptedLetter => {
const letterIndex = validLettersSplitted.indexOf(attemptedLetter);
if(letterIndex > -1){
validLettersSplitted.splice(letterIndex, 1);
return true;
} else {
return false
}
});
}
You are on the right way, You need to check each letter in the user word and check if they are in the random word. If one letter is in the random word, you remove it from the random word so it can't be use twice. :
let randomWord = "ABEOHSTD";
let userWordThatFail = "BAASE";
let userWord = "BASE";
// optionnaly, uppercase both words.
// we split into letters to make it easiers to process
let randomLetters = randomWord.split('');
let userLetters = userWord.split('');
let score = 0;
//we parse each letter of the user input
userLetters.forEach((letter) => {
// the letter exists in the random word.
let indexOfTheCurrentLetter = randomLetters.indexOf(letter);
// the letter exists, we remove it and increment the score.
if(indexOfTheCurrentLetter !== -1) {
randomLetters.splice(indexOfTheCurrentLetter, 1);
score++;
}
});
// the user input contains letters that are not in the random input.
if(score < userLetters.length) {
console.log('fail');
} else {
console.log('win : ' + score);
}
A simple approach might be to iterate the list of valid characters for every character encountered in the string to test. Using string.includes would fall into this bracket. Problematically, that's O(n_validchars * n_testchars) time-complexity for each comparison. This might not be desirable for longer strings.
The JavaScript Set object can help here.
With this higher-order function (that leans heavily on the iterable nature of a string), you can generate a reusable function for a set of valid characters:
function testFor(validChars) {
const charSet = new Set(validChars);
return testString =>
Array.prototype.every.call(testString, c => charSet.has(c));
}
// And to use it:
const testForABC = testFor("ABC"); //returns a function
console.log(testForABC("AABBCC")); //true
console.log(testForABC("abc")); //false
Now, because Set lookups are O(1), we're looking at O(n) complexity where n is the length of the string we're testing. Much better.

How to remove string between two characters every time they occur [duplicate]

This question already has answers here:
Strip HTML from Text JavaScript
(44 answers)
removing html tags from string
(3 answers)
Closed 7 years ago.
I need to get rid of any text inside < and >, including the two delimiters themselves.
So for example, from string
<brev-y>th</brev-y><sw-ex>a</sw-ex><sl>t</sl>​
I would like to get this one
that
This is what i've tried so far:
var str = annotation.split(' ');
str.substring(str.lastIndexOf("<") + 1, str.lastIndexOf(">"))
But it doesn't work for every < and >.
I'd rather not use RegEx if possible, but I'm happy to hear if it's the only option.
You can simply use the replace method with /<[^>]*>/g.It matches < followed by [^>]* any amount of non> until > globally.
var str = '<brev-y>th</brev-y><sw-ex>a</sw-ex><sl>t</sl>';
str = str.replace(/<[^>]*>/g, "");
alert(str);
For string removal you can use RegExp, it is ok.
"<brev-y>th</brev-y><sw-ex>a</sw-ex><sl>t</sl>​".replace(/<\/?[^>]+>/g, "")
Since the text you want is always after a > character, you could split it at that point, and then the first character in each String of the array would be the character you need. For example:
String[] strings = stringName.split("<");
String word = "";
for(int i = 0; i < strings.length; i++) {
word += strings[i].charAt(0);
}
This is probably glitchy right now, but I think this would work. You don't need to actually remove the text between the "<>"- just get the character right after a '>'
Using a regular expression is not the only option, but it's a pretty good option.
You can easily parse the string to remove the tags, for example by using a state machine where the < and > characters turns on and off a state of ignoring characters. There are other methods of course, some shorter, some more efficient, but they will all be a few lines of code, while a regular expression solution is just a single replace.
Example:
function removeHtml1(str) {
return str.replace(/<[^>]*>/g, '');
}
function removeHtml2(str) {
var result = '';
var ignore = false;
for (var i = 0; i < str.length; i++) {
var c = str.charAt(i);
switch (c) {
case '<': ignore = true; break;
case '>': ignore = false; break;
default: if (!ignore) result += c;
}
}
return result;
}
var s = "<brev-y>th</brev-y><sw-ex>a</sw-ex><sl>t</sl>";
console.log(removeHtml1(s));
console.log(removeHtml2(s));
There are several ways to do this. Some are better than others. I haven't done one lately for these two specific characters, so I took a minute and wrote some code that may work. I will describe how it works. Create a function with a loop that copies an incoming string, character by character, to an outgoing string. Make the function a string type so it will return your modified string. Create the loop to scan from incoming from string[0] and while less than string.length(). Within the loop, add an if statement. When the if statement sees a "<" character in the incoming string it stops copying, but continues to look at every character in the incoming string until it sees the ">" character. When the ">" is found, it starts copying again. It's that simple.
The following code may need some refinement, but it should get you started on the method described above. It's not the fastest and not the most elegant but the basic idea is there. This did compile, and it ran correctly, here, with no errors. In my test program it produced the correct output. However, you may need to test it further in the context of your program.
string filter_on_brackets(string str1)
{
string str2 = "";
int copy_flag = 1;
for (size_t i = 0 ; i < str1.length();i++)
{
if(str1[i] == '<')
{
copy_flag = 0;
}
if(str1[i] == '>')
{
copy_flag = 2;
}
if(copy_flag == 1)
{
str2 += str1[i];
}
if(copy_flag == 2)
{
copy_flag = 1;
}
}
return str2;
}

How to match a input parenthesis with regular expression in JavaScript?

I have no idea on matching the input parenthesis with JavaScript.
Input string example:
(pen)
((pen) and orange)
it should return false if the input string is like the following:
(pen
pen)
(pen) and orange)
((pen and orange )
((pen) and orange
)(pen and orange )(
(pen and )orange()
Regular expressions would be messy. It's much easier to go through with a simple counter.
function parenthesesBalanced(string) {
var count = 0;
for (var i = 0, l = string.length; i < l; i++) {
var char = string.charAt(i);
if (char == "(") {
// Opening parenthesis is always OK
count++;
} else if (char == ")") {
// If we're at the outer level already it's not valid
if (count == 0) return false;
count--;
}
}
return (count == 0);
}
replace every group of "left paren - some chars - right paren" with nothing, until there is no more groups. If the resulting string contains a parenthesis, the parens were not balanced.
balancedParens = function(str) {
var q;
do {
q = str;
str = str.replace(/\([^()]*\)/g, '');
} while(q != str);
return !str.match(/[()]/);
}
a = "foo ((and) bar and (baz) quux) and (blah)";
b = "(dddddd()";
alert(balancedParens(a))
alert(balancedParens(b))
http://jsfiddle.net/gvGGT/
It's not possible to match a balanced string with a single regexp in javascript, because JS dialect doesn't support recursive expressions.
It is a known hard problem to match parens with regular expressions. While it is possible, it's not particularly efficient.
It's much faster simply to iterate through the string, maintaining a counter, incrementing it every time you hit an open paren, and decrementing it every time you hit a close paren. If the counter ever goes below zero, or the counter is not zero at the end of the string, it fails.
I made a node library call balanced to make this a bit more sane, if you wanted to just get balanced outer matches you can do this
balanced.matches({source: source, open: '(', close: ')'})
my use case was a bit more complicated, and required me to do replacements and support comments.

Categories

Resources