Find if the sentence has 3 consecutive words - javascript

You are given a string with words and numbers separated by whitespaces (one space). The words contains only letters. You should check if the string contains three words in succession. For example, the string "start 5 one two three 7 end" contains three words in succession.
Input : String
Output : Boolean
This is what I'm trying to do, please point out my mistake. Thanks.
function threeWords(text){
let lst = text.split(' ');
for (let i=0; i< 3; i++) {
if (typeof lst[i] === 'string' && Number(lst[i]) === NaN) {return true}
else {return false;}}
}

If you'd rather continue with your code than use regex, here are your issues:
You only loop over 3 of the elements in lst. Loop over the entire length of the list.
You try to check if Number('somestring') === NaN. In JavaScript, NaN === NaN is False. Use isNaN() instead.
Once you find a list element that is not a number, you return True. You should have a variable that keeps track of how many words there are in succession (resetting to 0 when you find a number), and return True when this variable is equal to 3.
Here is the fixed code:
function threeWords(text) {
let lst = text.split(' ');
let num_words = 0;
for (let i = 0; i < lst.length; i++) {
if (isNaN(Number(lst[i])))
num_words++
else
num_words = 0
if (num_words === 3)
return true
}
return false;
}

Might be easier with a regular expression:
const result = /([A-Za-z]+( |$)){3}/.test('start 5 one two three 7 end');
console.log(result);

Related

Hangman Game Javascript: Word completes if last character in word is guessed

I am coding a hangman game for a class assignment. The wordcheck() function, which compares the index of the characters of the word to the index of the spaces, is not working as it should. If the user guesses a letter in the middle of the word, the letter will fill the _ space just as it should. However, if the user guesses the last letter of the word, it will automatically go to the win screen and reveal the full word even if the spaces before do not match. I am positive the problem is in this function. I believe it's because the loop runs for however long the characters list is (or how long the word is) and if the last value returns as equal to the last value in the spaces list, it will return as true even if the previous values return as unequal. Still, even if this is the problem, I have no idea how to fix it.
Thank you!
function wordCheck() {
var wordComplete = false;
for (var i = 0; i < letters.length; i++) {
if (letters[i] !== blanks[i]) {
wordComplete = false;
}
else {
wordComplete = true;
}
}
if (wordCompleted == true) {
gameWon();
}
}
The problem is, you need to set the value of true ONLY if the condition for true exists (and in this case, you only know that for a fact AFTER you have evaluated all the characters and spaces). In your code, you set to either true or false EVERY time you evaluate a characters/space match (which means you can unset the result you are looking for). This little logic trap gets coders all the time.
Consider it this way: I get to ask your name five times, and I only have to get it right once to win, but I always have to guess five times (after which you will tell me if one of my guesses was correct). If you evaluate my answer each time, and the last answer is wrong, it will never matter if I ever got it right. It will only work if the last guess is correct. So you would either stop evaluating answers once I've guessed correctly, of you would wait for all five guesses and THEN determine if I win/lose.
In your case, you require that all "guesses" be made before determining win/lose. So you just track them, and afterwards, make the win/lose decision.
So you'd do something like this:
function wordCheck(){
// track the matches
var matches = 0;
for ( var i = 0; i < spaces.length; i++ ) {
// If there's a match, increment matches. Else, leave it alone.
matches = ( characters [ i ] === spaces [ i ] ? matches + 1 : matches );
}
// Now that you have evaluated ALL matches, you can make the decision.
// If matches === your spaces length, all matches were found, return true, else false
return ( matches === spaces.length );
}
function wordCheck() {
wordCompleted = true; //set it to true first,
for (var i = 0; i < characters.length; i++) {
if (characters[i] !== spaces[i]) {
wordCompleted = false; //then, if a single char is false, set it to false
}
}
if (wordCompleted == true) {
gameWon();
}
}
Your issue was that you were setting wordCompleted to true if a letter was right. This meant that if only the last letter was correct, it would still be set to true (there were no false letters after to set it to false)
Yes, indeed your function is changing the value of wordCompleted in each iteration. If the last char matches, then wordCompleted is true, and you call gameWon(). I think the expected behavior would be if just call gameWon() when the whole word is guessed, right? I think this version of your function make what you want.
function wordCheck(){
let i = 0;
while(i < characters.length && characters[i] === spaces[i]){
i++;
}
const wordCompleted = i === characters.length;
if (wordCompleted == true) {
gameWon();
}
}
Now with this function, we are iterating over the characters and spaces arrays until their elements are different or the whole array was iterated. Then we just check if we reach the last element, if yes, the game is won.
You're partially correct that the problem is that you continue through the loop to the end - the thing is, the way you've set up the function means that only the last character matters.
What you really care about though, is how many of the characters in the entire word match.
Take a look at this function:
function wordCheck(){
var correctCharacters = 0;
for (var i = 0; i < characters.length; i++) {
if (characters[i] === spaces[i]) {
correctCharacters ++;
}
}
if (correctCharacters === characters.length) {
gameWon();
}
}
If there is a correct letter after a bad letter your script says all your word is correct because you will set the boolean for each letters hence override the bad letter check.
So just define a boolean to true at the beginning and then set it to false if a letter if wrong but don't set it back to true if the letter is right. At then end if your boolean is false, it means one of your letter is wrong.
function wordCheck(){
var wordCompleted = true;
for (var i = 0; i < characters.length; i++) {
if (characters[i] !== spaces[i]) {
wordCompleted = false;
}
}
if (wordCompleted == true) {
gameWon();
}
}

Count characters in strings with or without string.slice

I am trying to learn how this problem has been solved but I am lost at string.slice(0,1) and i++ after that.
What is the need to use the slice method?
The questions is:
Write a function called countChars that accepts two parameters: a string and a character. This function should return a number representing the number of times that the character appears in string.
function countChars(string, character) {
let count = 0;
let i = 0;
while (i < string.length) {
if (string[i] === character) {
count++;
}
string.slice(0, 1);
i++;
}
return count;
}
Nina already solved it using your code, a shorter option would be using String.match
function countChars(string, character) {
const { length } = string.match(new RegExp(character, 'ig')) || [];
return length;
}
console.log(countChars('Foobar', 'o'));
Explanation of your code:
function countChars(string, character) {
let count = 0; // set variable count to zero
let i = 0; // set variable i to zero
while (i < string.length) { // loop while i is less than lenght of string
if (string[i] === character) { // check character at position i
count++; // Increment count by one
}
string.slice(0, 1); // returns the first character in string, but return value is not used. Can be removed
i++; // increment variable i by one
}
return count; // returns count
}
console.log("Count", countChars("ABCDABC", "A"));
string.slice(0, 1) The slice method extracts a section of a string and returns it as a new string, without modifying the original string. The return value is not used in this function, so you can remove the call. I guess that the person who wrote it tried to remove the first character of the string for each iteration of the loop. That is not a good way to solve the problem, for example because it creates one string per iteration and wastes memory.
i++ The increment operator ++ increments (adds one to) its operand and returns a value. There are two ways to use this operator: as pre increment ++i (returns the value before incrementing) or post increment i++ (returns the value after incrementing). Both variants are useful. When used on a single line (like in your example) it doesn't matter. You can also write it as i = i + 1 or using the addition assignment i += 1.
let value = 0;
console.log( 'Pre increment: before %d, result %d, after %d', value, ++value, value); // before 0, result 1, after 1
value = 0;
console.log( 'Post increment: before %d, result %d, after %d', value, value++, value); //before 0, result 0, after 1
Using the while-statement is one way to create the loop that iterates over the string, using the for-statement is another, more compact way.
Example with while:
let i = 0; // initialization
while ( i < string.length ) { // check contition
// ... some code ...
i += 1;
}
Example with for:
for ( let i = 0; i < string.length; i += 1 ) {
// ... some code ...
}
The pros with for are: the initialization, condition and final expression is defined in the header. The scope of the i variable can with let be restricted to be defined only inside the for-loop.
Your example, but with a for-loop:
function countChars(string, character) {
let count = 0; // set variable count to zero
for (let i = 0; i < string.length; i += 1) {
if (string[i] === character) { // check character at position i
count++; // Increment count by one
}
}
return count; // returns count
}
console.log("Count", countChars("ABCDABC", "A"));
The code is buggy!
There is a bug with this solution, and it has to do with how characters are defined. In Javascript each position of the string is a 16-bit integer. It works for most text, but fails for emojis and other characters that aren't in the Unicode Basic Multilingual Plane since they are defined with two positions in the string (surrogate pairs).
A solution that works is to use the split-method, that splits a string object into an array of strings, using a specified separator string to determine where to make each split. Note that if the separator string isn't found, or if the input string is empty, an array of one string will be returned. If there is one match, the returned array will have two strings. So the split-method returns a value that is one more than what we want, but that is easy to fix by just subtracting one.
function countChars(string, character) {
return string.split(character).length - 1;
}
console.log("Count", countChars("ABCD😃ABC😃", "😃"));
Optimizations
While “Premature optimization is the root of all evil”, there are some things that can be written in a clean and optimized way while you write the code. For example, if you are writing to a file, it is very inefficient to open the file, write something, and then close the file inside the loop:
for( i = 0; i < array.length; i + i = 1) {
fs.openFile("log.txt").write(array[i]).close();
}
It should be no surprise that it is more efficient to first open the file, write inside the loop, and then close the file:
fh = fs.openFile("log.txt");
for( i = 0; i < array.length; i + i = 1) {
fh.write(array[i]);
}
fh.close();
The reason I mention this, is because when I write a for-statement, I usually initialize both the variable and the length in the initialization part of the for-statement, since the length of the string doesn't change (and using the property string.length is always more expensive than using a local variable).
for (let i = 0, length = string.length; i < length; i += 1) {
// ... code ...
}
Another thing: string[i] and string.charAt(i) creates a new string in every iteration of the loop. To compare a single character inside a loop, it is much faster to compare integers instead of strings, by using string.charCodeAt(i). And instead of string.charCodeAt(i) you can use string.codePointAt(i) to make it safe to use with all Unicode characters like emoji, not only the characters in the BMP.
As I said above, the method that you used isn't safe to use with Unicode. For example, if you search for an emoji (😃), you will not get the correct result.
The following two methods are safe to use with all Unicode codepoints. Both of them can handle a needle with multiple characters.
function count_split(haystack, needle) {
return haystack.split(needle).length - 1;
}
This function uses a regular expression as the needle, and that can give unexpected results, since some characters have special meaning. For example, if you search for a dot (.) it will match every character. On the other hand, you can do an advanced search for '[ab]' that will match all 'a' and 'b' characters.
function count_match(haystack, needle) {
const match = haystack.match(new RegExp(needle, 'g')) || [];
return match.length;
}
The following method is safe to use with any Unicode, and the needle must be a single codepoint.
function countChars(haystack, needle) {
let count = 0;
const codePoint = needle.codePointAt(0);
if (
!((needle.length === 1) || (needle.length === 2 && codePoint > 0xffff))
) {
// NOTE: Using charPointAt(0) returns the codePoint of the first character.
// Since the characters in the string is stored as 16-bit integers,
// characters outside of the Unicode BMP is stored as surrogate pairs
// in two positions. Thats why the length of the string is1 for
// a BMP codepoint and 2 for a codepoint outside of the BMP.
// codePointAt handles the decoding of the surrogate pair.
throw Error('needle should be one codepoint');
}
for (let i = 0, length = haystack.length; i < length; i += 1) {
if (haystack.codePointAt(i) === codePoint) {
count++;
}
}
return count;
}
I have created test-cases on jsperf.com to compare the speed of the Unicode safe-functions mentioned above, and also the UNSAFE-functions that where published in this thread while I was writing this answer.
Without slice
function countChars(string, character) {
let count = 0;
let i = 0;
while (i < string.length) {
if (string[i] === character) {
count++;
}
i++;
}
return count;
}
console.log(countChars('foobar', 'o'))
With slice, but without i.
function countChars(string, character) {
let count = 0;
while (string.length) { // check if the length is not zero
if (string[0] === character) {
count++;
}
string = string.slice(1); // take the rest of the string, beginning from index 1
}
return count;
}
console.log(countChars('foobar', 'o'))
If you're willing to look for other solutions, here is a short one.
function count(str, char){
let count = str.split('').filter(c => {
return c.toLowerCase() === char.toLowerCase()
});
return count.length;
}
console.log(count('hello world', 'l'));
Note: The above solution is case-insensitive.
I would do it like this
function countChars(string, character) {
return string.split('').filter(e => e === character).length;
}
console.log(countChars('foobar', 'o'))
But #bambam RegEx approach is likely the most efficient.
A shorter version :)
function count(string,char) {
return string.split('').filter(x => x === char).length;
}
Thanks

JS: Write a function that will return the count of distinct case-insensitive alphabetic characters and numeric digits that occur more than once

I have the following problem: Count the number of Duplicates
Write a function that will return the count of distinct case-insensitive alphabetic characters and numeric digits that occur more than once in the input string. The input string can be assumed to contain only alphanumeric characters, including digits, uppercase and lowercase alphabets.
Example
"abcde" -> 0 (# no characters repeats more than once)
"aabbcde" -> 2 (# 'a' and 'b')
"aabbcdeB" -> 2 (# 'a' and 'b')
"indivisibility" -> (1 # 'i')
"Indivisibilities" -> 2 (# 'i' and 's')
"aa11" -> 2 (# 'a' and '1')
I have the code below which appears to work but I don't quite understand it:
function countDuplicates(str) {
var myObj = {},
counter = 0;
for(var i = 0; i < str.length; i++){
if(!myObj[str[i]]){
myObj[str[i]] = 1;
}
else if(myObj[str[i]] < 2) {
myObj[str[i]]+=1;
counter++;
}
}
return counter;
}
Would it be possible to go through line by line and provide a quick explanation of what the code is doing?
function duplicateCount(text) {
var nText = text.toLowerCase(); //this changes every character to lower case
var myObj = {}; // Declaring an empty object
var counter = 0;
for (var i = 0; i < nText.length; i++) {
// assigning character as property and giving value 1
if (!myObj[nText[i]]){
myObj[nText[i]] = 1;
}//checking if character already added
else if (myObj[nText[i]] < 2) {
myObj[nText[i]] += 1;
counter++;
}
}
return counter;
}
This code is going throught all characters in string
You are creating an empty object myObj
Adding property to myObj, with the same name as value of the currently checking character i.e. myObj[str[i]] you can decompose it as:
str[i] is equal g so you will have myObject['g']
and then assign number 1 to myObject['g'] so your object now looks like:
myObj: {
g: 1
}
Checking next charater of the string, now the charakter is e.g. c, so you are doing the same operation as in pt.3, now you object looks like:
myObj: {
g: 1, c: 1
}
But what happen if you already have an added character to myObj, this is checking in if(!myObj[str[i]]){...}, so what is happening here, is that we are checking if currently checked charaker is already added in the object. If not added then add it with value 1 as in pt.3 e.g. !myObj[str[i]] is the same as myObj['c'] === undefined (if is undefined, add it to object)
But if it is already added we are checking if the value of the object property is less then 2 (we want to increment counter only once for specified character), if yes we are incrementing counter (yep we have found the duplicate)
Return counter
function countDuplicates(text) {
return text.toLowerCase().split('').filter((val, i, arr) => {
return !!(arr.indexOf(val) !== i && arr.lastIndexOf(val) === i);
}).length;
}

Letter Count I JavaScript Challenge on Coderbyte

I've been on this problem for several hours now and have done all I can to the best of my current newbie javaScript ability to solve this challenge but I just can't figure out exactly what's wrong. I keep getting "UNEXPECTED TOKEN ILLEGAL on here: http://jsfiddle.net/6n8apjze/14/
and "TypeError: Cannot read property 'length' of null": http://goo.gl/LIz89F
I think the problem is the howManyRepeat variable. I don't understand why I'm getting it can't read the length of null when clearly word is a word from str...
I got the idea for:
word.toLowerCase().split("").sort().join("").match(/([.])\1+/g).length
...here: Get duplicate characters count in a string
The Challenge:
Using the JavaScript language, have the function LetterCountI(str) take the str
parameter being passed and return the first word with the greatest number of
repeated letters. For example: "Today, is the greatest day ever!" should return
greatest because it has 2 e's (and 2 t's) and it comes before ever which also
has 2 e's. If there are no words with repeating letters return -1. Words will
be separated by spaces.
function LetterCountI(str){
var wordsAndAmount={};
var mostRepeatLetters="-1";
var words=str.split(" ");
words.forEach(function(word){
// returns value of how many repeated letters in word.
var howManyRepeat=word.toLowerCase().split("").sort().join("").match(/([.])\1+/g).length;
// if there are repeats(at least one value).
if(howManyRepeat !== null || howManyRepeat !== 0){
wordsAndAmount[word] = howManyRepeat;
}else{
// if no words have repeats will return -1 after for in loop.
wordsAndAmount[word] = -1;
}
});
// word is the key, wordsAndAmount[word] is the value of word.
for(var word in wordsAndAmount){
// if two words have same # of repeats pick the one before it.
if(wordsAndAmount[word]===mostRepeatLetters){
mostRepeatLetters=mostRepeatLetters;
}else if(wordsAndAmount[word]<mostRepeatLetters){
mostRepeatLetters=mostRepeatLetters;
}else if(wordsAndAmount[word]>mostRepeatLetters){
mostRepeatLetters=word;
}
}
return mostRepeatLetters;
}
// TESTS
console.log("-----");
console.log(LetterCountI("Today, is the greatest day ever!"));
console.log(LetterCountI("Hello apple pie"));
console.log(LetterCountI("No words"));
Any guidance is much appreciated. Thank you!! ^____^
Here is the working code snippet:
/*
Using the JavaScript language, have the function LetterCountI(str) take the str
parameter being passed and return the first word with the greatest number of
repeated letters. For example: "Today, is the greatest day ever!" should return
greatest because it has 2 e's (and 2 t's) and it comes before ever which also
has 2 e's. If there are no words with repeating letters return -1. Words will
be separated by spaces.
console.log(LetterCountI("Today, is the greatest day ever!") === "greatest");
console.log(LetterCountI("Hello apple pie") === "Hello");
console.log(LetterCountI("No words") === -1);
Tips:
This is an interesting problem. What we can do is turn the string to lower case using String.toLowerCase, and then split on "", so we get an array of characters.
We will then sort it with Array.sort. After it has been sorted, we will join it using Array.join. We can then make use of the regex /(.)\1+/g which essentially means match a letter and subsequent letters if it's the same.
When we use String.match with the stated regex, we will get an Array, whose length is the answer. Also used some try...catch to return 0 in case match returns null and results in TypeError.
/(.)\1+/g with the match method will return a value of letters that appear one after the other. Without sort(), this wouldn't work.
*/
function LetterCountI(str){
var wordsAndAmount={};
var mostRepeatLetters="";
var words=str.split(" ");
words.forEach(function(word){
var howManyRepeat=word.toLowerCase().split("").sort().join("").match(/(.)\1+/g);
if(howManyRepeat !== null && howManyRepeat !== 0){ // if there are repeats(at least one value)..
wordsAndAmount[word] = howManyRepeat;
} else{
wordsAndAmount[word] = -1; // if no words have repeats will return -1 after for in loop.
}
});
// console.log(wordsAndAmount);
for(var word in wordsAndAmount){ // word is the key, wordsAndAmount[word] is the value of word.
// console.log("Key = " + word);
// console.log("val = " + wordsAndAmount[word]);
if(wordsAndAmount[word].length>mostRepeatLetters.length){ //if two words have same # of repeats pick the one before it.
mostRepeatLetters=word;
}
}
return mostRepeatLetters ? mostRepeatLetters : -1;
}
// TESTS
console.log("-----");
console.log(LetterCountI("Today, is the greatest day ever!"));
console.log(LetterCountI("Hello apple pie"));
console.log(LetterCountI("No words"));
/*
split into words
var wordsAndAmount={};
var mostRepeatLetters=0;
loop through words
Check if words has repeated letters, if so
Push amount into object
Like wordsAndAmount[word[i]]= a number
If no repeated letters...no else.
Loop through objects
Compare new words amount of repeated letters with mostRepeatLetters replacing whoever has more.
In the end return the result of the word having most repeated letters
If all words have no repeated letters return -1, ie.
*/
The changes made:
[.] turned into . as [.] matches a literal period symbol, not any character but a newline
added closing */ at the end of the code (the last comment block was not closed resulting in UNEXPECTED TOKEN ILLEGAL)
if(howManyRepeat !== null || howManyRepeat !== 0) should be replaced with if(howManyRepeat !== null && howManyRepeat !== 0) since otherwise the null was testing for equality with 0 and led to the TypeError: Cannot read property 'length' of null" issue. Note that .match(/(.)\1+/g).length cannot be used since the result of matching can be null, and this will also cause the TypeError to appear.
The algorithm for getting the first entry with the greatest number of repetitions was wrong since the first if block allowed subsequent entry to be output as a correct result (not the first, but the last entry with the same repetitions was output actually)
-1 can be returned if mostRepeatLetters is empty.
Hope you dont mind if I rewrite this code. My code may not be that efficient.
Here is a snippet
function findGreatest() {
// ipField is input field
var getString = document.getElementById('ipField').value.toLowerCase();
var finalArray = [];
var strArray = [];
var tempArray = [];
strArray = (getString.split(" "));
// Take only those words which has repeated letter
for (var i = 0, j = strArray.length; i < j; i++) {
if ((/([a-zA-Z]).*?\1/).test(strArray[i])) {
tempArray.push(strArray[i]);
}
}
if (tempArray.length == 0) { // If no word with repeated Character
console.log('No such Word');
return -1;
} else { // If array has words with repeated character
for (var x = 0, y = tempArray.length; x < y; x++) {
var m = findRepWord(tempArray[x]); // Find number of repeated character in it
finalArray.push({
name: tempArray[x],
repeat: m
})
}
// Sort this array to get word with largest repeated chars
finalArray.sort(function(z, a) {
return a.repeat - z.repeat
})
document.getElementById('repWord').textContent=finalArray[0].name;
}
}
// Function to find the word which as highest repeated character(s)
function findRepWord(str) {
try {
return str.match(/(.)\1+/g).length;
} catch (e) {
return 0;
} // if TypeError
}
Here is DEMO
function LetterCountI(str) {
var word_arr = str.split(" ");
var x = word_arr.slice();
for(var i = 0; i < x.length; i ++){
var sum = 0;
for(var y = 0; y < x[i].length; y++){
var amount = x[i].split("").filter(function(a){return a == x[i][y]}).length;
if (amount > 1){
sum += amount
}
}
x[i] = sum;
}
var max = Math.max.apply(Math,x);
if(max == 0)
return -1;
var index = x.indexOf(max);
return(word_arr[index]);
};
Here is another version as well.
You could use new Set in the following manner:
const letterCount = s => {
const res = s.split(' ')
.map(s => [s, (s.length - new Set([...s]).size)])
.reduce((p, c) => (!p.length) ? c
: (c[1] > p[1]) ? c : p, []);
return !res[1] ? -1 : res.slice(0,1).toString()
}
Note: I have not tested this solution (other than the phrases presented here), but the idea is to subtract unique characters from the total characters in each word of the phrase.

CoderByte SimpleSymbols challenge: pattern recognition in strings (using RegExp)

The problem:
Have the function SimpleSymbols(str) take the str parameter being passed and determine if it is an acceptable sequence by either returning the string true or false. The str parameter will be composed of + and = symbols with several letters between them (ie. ++d+===+c++==a) and for the string to be true each letter must be surrounded by a + symbol. So the string to the left would be false. The string will not be empty and will have at least one letter.
My code:
function SimpleSymbols(str) {
var arr = str.match(/[\+][a-zA-Z][\+]/g);
var total = str.match(/[a-zA-Z]/g);
if(arr === null || total === null)
return false;
else if(arr.length >= 1 && arr.length === total.length)
return true;
else
return false;
}
All the test cases except for these three pass:
-"+z+z+z+"
-"2+a+a+"
-"+z+z+==+a+"
What I've done: checked the other question on this problem. Tried another solution using regex but it had issues with input like "b".
I think the problem has something to do with when the pattern is "+char+char+" since a lot of the other test cases are like "++char+==+char+=="

Categories

Resources