I have a function which checks if a given character is capital letter and returns true of false value:
function isUpperCase(aCharacter)
{
return (aCharacter >= 'A') && (aCharacter <= 'Z');
}
Now I have a string of characters e.g. ThksAbcdEvat.
I want to write a function which checks every character in a string and when it encounters a capital letter is will execute function decryptW but only on a block of letters until next capital letter.
Function decryptW works fine on single words. So what im looking for is execution of function 'decryptW' on 'Thks' 'Abcd' 'Evat' and return 3 words as a result. all i have at the moment is:
function decryptMessage(cipherText, indexCharacter, plainAlphabet, cipherAlphabet)
{
for (var count = 0, count < cipherText.length; count++)
{
while (isUpperCase(cipherText.charAt(count))
{
if (count - lastCapital > 1)
{
decryptWord(cipherText, indexCharacter, plainAlphabet, cipherAlphabet);
lastCapital = count;
}
}
}
}
Can you tell me if I'm even close to what I want to achieve? Any help would be much appreciated.
Probably regular expression can help you
var re = /[A-Z][a-z]*/;
var s = 'ThksAbcdEvat';
s.replace(re, function(c){
//do something with c
return c;
});
For what you describe (if I understood right) using String.replace/split will do the job of splitting up the string on its capitals:
'ThksAbcdEvat'.replace(/(.(?=[A-Z]))/g,'$1,').split(',');
//=> Thks,Abcd,Evat
Where /(.(?=[A-Z]))/g means: find any character followed by a capital A to Z, and the replacement ('$1,') means: add a ',' (comma) to the found character(s).
After that you can run a loop to apply decryptWord to every word in the array (/g means: global, i.e. do that for the whole string). So your whole decryptMessage function could look like:
function decryptMessage(cipherText /*,... other params*/ ){
var captalWords = cipherText.replace(/(.(?=[A-Z]))/g,'$1,').split(',');
for (var i=0;i<capitalWords.length;i++){
decryptWord(captalWords[i] /*,... other params*/ );
}
}
I'd say, no real need for complex while loops etc. here.
Related
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();
}
}
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
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.
My whole goal was to write a loop that would take a string, count the letters and return two responses: one = "this word is symmetric" or two = "this word is not symmetric". However the code I wrote doesn't console anything out. Here's the code:
var arya = function(arraycount){
for (arraycount.length >= 1; arraycount.length <= 100; arraycount++) {
while (arraycount.length%2 === 0) {
console.log("This is a symmetric word and its length is " + " " arraycount.length " units.");
arraycount.length%2 != 0
console.log("Not a symmetric word");
}
}
}
arya("Michael");
There are many ways to accomplish your goal, but here are a few. The first is a somewhat naïve approach using a for loop, and the second uses recursion. The third asks whether the string equals the reverse of the string.
iterative (for loop) function
var isPalindromeIteratively = function(string) {
if (string.length <= 1) {
return true;
}
for (var i = 0; i <= Math.floor(string.length / 2); i++) {
if (string[i] !== string[string.length - 1 - i]) {
return false;
}
}
return true;
};
This function begins by asking whether your input string is a single character or empty string, in which case the string would be a trivial palindrome. Then, the for loop is set up: starting from 0 (the first character of the string) and going to the middle character, the loop asks whether a given character is identical to its partner on the other end of the string. If the parter character is not identical, the function returns false. If the for loop finishes, that means every character has an identical partner, so the function returns true.
recursive function
var isPalindromeRecursively = function(string) {
if (string.length <= 1) {
console.log('<= 1');
return true;
}
var firstChar = string[0];
var lastChar = string[string.length - 1];
var substring = string.substring(1, string.length - 1);
console.log('first character: ' + firstChar);
console.log('last character: ' + lastChar);
console.log('substring: ' + substring);
return (firstChar === lastChar) ? isPalindromeRecursively(substring) : false;
};
This function begins the same way as the first, by getting the trivial case out of the way. Then, it tests whether the first character of the string is equal to the last character. Using the ternary operator, the function, returns false if that test fails. If the test is true, the function calls itself again on a substring, and everything starts all over again. This substring is the original string without the first and last characters.
'reflecting' the string
var reflectivePalindrome = function(string) {
return string === string.split('').reverse().join('');
};
This one just reverses the string and sees if it equals the input string. It relies on the reverse() method of Array, and although it's the most expressive and compact way of doing it, it's probably not the most efficient.
usage
These will return true or false, telling you whether string is a palindrome. I assumed that is what you mean when you say "symmetric." I included some debugging statements so you can trace this recursive function as it works.
The Mozilla Developer Network offers a comprehensive guide of the JavaScript language. Also, here are links to the way for loops and while loops work in JS.
If I got a Letter in JavaScript, I'd like to find out the previous letter in alphabetic order, so if input is "C", output must be "B". Are there any standard solutions or do i have to create some special functions?
var ch = 'b';
String.fromCharCode(ch.charCodeAt(0) - 1); // 'a'
And if you wanted to loop around the alphabet just do a check specifically for 'a' -- loop to 'z' if it is, otherwise use the method above.
This should work in some cases, you might need to tweak it a bit:
function prevLetter(letter) {
return String.fromCharCode(letter.charCodeAt(0) - 1);
}
If letter is A, the result is #, so you need to add some sanity checking if you want it to be foolproof. Otherwise should do the job just fine.
The full function from Tatu's comment would be
function prevLetter(letter) {
if (letter === 'a'){ return 'z'; }
if (letter === 'A'){ return 'Z'; }
return String.fromCharCode(letter.charCodeAt(0) - 1);
}
Something like this should work.
function prevLetter(letter) {
var code = letter.charCodeAt(0);
var baseLetter = "A".charCodeAt(0);
if (code>"Z".charCodeAt(0)) {
var baseLetter = "a".charCodeAt(0);
}
return String.fromCharCode((code-baseLetter+25)%26+baseLetter);
}