indexOf() function to return a -1 value - javascript

I have a script that I'm trying to get working. Basically, what I'm trying to do is prevent someone from entering in special characters in to a field.
the function I have is as follows:
var iChars = "!##$%^&*()+=[];,./{}|<>?;";
if (field1.value.indexOf(iChars) !=-1)
{
alert ("problem")
}
The issue I'm having is that the field is searching for the exact match to the iChars var instead of matching any single value. For example if I create a var test ="one" and enter "one" into the field, it returns with the error, but if I enter "o" into the field, it does not return anything and just passes through to the next portion of the script - but if i entered "none" or "oneeee" it would push the error through.
Any help on fixing this? I tried looking into arrays for indexOf but didn't really understand it, so if you are going to suggest it can you please explain in as much detail as possible.
Thanks

You could use...
var value = field1.value;
for (var i = 0, length = value.length; i < length; i++) {
if (iChars.indexOf(value.charAt(i)) != -1) {
alert('Problem!');
break;
}
}

The problem is you are looking for the index of the entire iChars string in the user input. What you actually want to do though is see if any char in iChars is in the input string. To do this use a for loop
var iChars = "!##$%^&*()+=[];,./{}|<>?;";
var i;
for (i = 0; i < iChars.length; i++) {
if (field1.value.indexOf(iChars[i]) !== -1) {
alert("problem");
break;
}
}

You can also just check against a regular expression:
var iChars = /[!##$%^&*()+=[\];,./{}|<>?;]/;
if (iChars.test(field1.value)) {
alert("problem");
}
This route should be a little more efficient than iterating over each character in the string yourself, and to the regex-trained eye (or at least in my obviously-biased opinion) is simpler to read.

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();
}
}

Javascript: How can I selectively convert a string of characters into their respective unicode values, add 1, and then convert back to string?

I'm able to successfully target only the letters in my string, however I am having trouble converting only the letters to their unicode values. Please help.
function LetterChanges(str) {
for(var i = 0; i < str.length; i++){
if(str.charCodeAt(i) > 64 && str.charCodeAt(i) < 127){
str.repalce(i, charCodeAt(i));
}
}
console.log(str)
}
LetterChanges("hello*3");
function LetterChanges(str) {
var newStr = ""; // the result string
for (var i = 0; i < str.length; i++) {
var c = str.charCodeAt(i);
if (c > 64 && c < 127) {
newStr += String.fromCharCode(c + 1);
}
else {
newStr += String.fromCharCode(c);
}
}
return newStr;
}
console.log(LetterChanges("hello*3"));
If you want to replace only alphabetic characters a-z, you can do it using a regular expression like this:
function LetterChanges(str) {
return str.replace(/[a-z]/gi, function(m) {
return String.fromCharCode(
m.charCodeAt(0) + 1
);
});
}
console.log(LetterChanges("Hello*3"));
I actually think this looks a lot better as a one liner. I actually needed to do this in an interview recently and they seemed pretty impressed by the conciseness of my solution
So instead I would use this:
function letterChange(str){
return String.fromCharCode.apply(null,Array.prototype.map.call(str,i=>i.charCodeAt(0)+1));
}
Also not that the i=>u.charCodeAt(0) is a lambda function and will not work in all web browsers without use of a transpiler. You could also use this if you are still using es5:
function letterChange(str){
return String.fromCharCode.apply(null,Array.prototype.map.call(str,function(i){
return i.charCodeAt(0)+1;
}));
}
To explain this a little more lets break it down:
the first thing that may raise questions is the 'apply' function. Apply takes 2 parameters, an object (irrelevant in this case so we use null), and an array of parameters. These parameters are then passed in comma separated form to the function that we call apply on. In the case of fromCharCode, passing multiple parameters returns a String composed of all of the results. Next, we are passing it 'Array.prototype.map.call' which allows us to call the array literal function map on a String which does not come with map out of the box. We then pass this the function to apply and return the full result.

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.

JAVASCRIPT HELP - code not working

My code isn't working, can someone please tell me what the problem is?
I'm guessing it's the for loop, but I cannot find the problem.
<html>
<body>
<script>
username = prompt("Please enter a your username:");
for (var i = 0; i < username; i++) {
if(isFinite(username.charAt(i))) {
result = true;
document.write("The username consists of one or more numbers." + BR);
}
else {
result = false;
document.write("The username must consist of one or more numbers." + BR);
}
}
</script>
</body>
</html>
You have two problems in your code:
In the for loop, use the length of the variable to establish the stop condition
for (var i = 0; i < username.length; i++)
BR is not defined
Working code: http://jsfiddle.net/f643fr4w/
From the output I can probably assume you just want to check if username consists of at least one number, actually: a digit.
// iterate over the input
for (var i = 0; i < username.length; i++) {
// check if it is a number (not a digit but that's the same here)
if (isFinite(username.charAt(i))) {
result = true;
// The requirement "one or more numbers" is fulfilled,
// we can break out of the loop
break;
}
else {
result = false;
}
// print something according to "result"
if(result === true){
document.write('The username consists of one or more numbers.');
} else {
document.write('The username must consist of one or more numbers.');
}
}
You have to go over the full length of the string to find out if there's no number but not if you want to find out if there is any number in it.
Now, if you want to test if it consists of only digits you have to reword the requirements, they are a bit too ambiguous now.
Additional hints:
you need to check the input, you always have to check user input!
you need to be aware that JavaScript strings are UTF16. Rarely a problem but gets easily one if you iterate over JavaScript strings.
String.charAt() returns a character, not a number. Don't rely on the automatic conversions in JavaScript, you way too easily shoot yourself in the foot if you rely on it but also if you don't, so be careful.
please don't use document.write, use either the console if available or change the text-node of an HTML element.
With these points in mind you may get something like this:
// make a list of digits
var digits = ['0','1','2','3','4','5','6','7','8','9'];
// ask the user for a username
var username = prompt("Please enter a your username:");
// check input
if (username.length === 0) {
console.log('no username given');
} else {
for (var i = 0; i < username.length; i++) {
// indexOf searches for the same type, that's why the digits above
// are strings with quotes around them
if (digits.indexOf(username.charAt(i)) >= 0) {
result = true;
// The requirement "one or more numbers" is fullfilled,
// we can break out of the loop
break;
}
else {
result = false;
}
}
// print something according to "result"
if (result === true) {
console.log('The username consists of one or more numbers.');
} else {
console.log('The username must consist of one or more numbers.');
}
}
The above is one variation of many and could easily give rise to a heated discussion on some forums (not here! Of course not! ;-) ) but I hope it helps.
Use a regex for such shenanigans:
var username = prompt("username plz kk thx");
var result = /[0-9]/.test(username);
document.write("The username " + (result ? "consists" : "must consist") + " of one or more numbers");

Find all instances and display alert - part 2, now with regex

Thanks for your help with my earlier question:
How to find all instances and display in alert
Now I discover that I need to include some invalid character validation.
I'm trying to figure out how to include a set of regex invalid characters as part of the validation that will also show up in the same alert/textbox/whatever as the "too long/too short" validation.
So, I have a textbox which users will type or paste comma separated values such as AAAAAAA,BBBBBBB,CCCCCCCC,DDDDDDDD
And they cannot be more or less than seven characters long and they can only include certain characters.
I currently have have two separate pieces of Javascript that I'm trying to now combine:
var Invalidchars = "1234567890!##$%^&*()+=[]\\\';./{}|\":<>?";
for (var i = 0; i < document.getElementById("TextBox1").value.length; i++) {
if (Invalidchars.indexOf(document.getElementById("TextBox").value.charAt(i)) != -1){
alert
and this
var val = document.getElementById("Textbox1").value,
err = $.grep(val.split(','), function(a) { return a.length != 7; });
if (err.length) {
alert("All entries must be seven (7) characters in length. Please correct the following entries: \n" + err);
return false;
}
return true;
Any help is much appreciated!
=================================================
SOLUTION
Took a while, but using Tenub's code (which didn't quite combine my two sets code, but was close enough), I finally figured out how to merge my two sets of code into one. Here's the code if anyone is ever interested in using it:
var val = document.getElementById("TextBox1").value,
err = $.grep(val.split(','), function(a) {return (a.length = (!/^[^0-9!##$%^&*()+=;.\/\{}|:<>\\?\[\]\'\"]{7}$/.test(a)));});
if (err.length){
document.getElementById("DIV1").style.display = "inline-block";
document.getElementById("TextBox2").value = err.join(',');
return callback (false);
}
document.getElementById("DIV1").style.display = "none";
return true;
The answer is as simple as it is elegant:
var val = document.getElementById("Textbox1").value;
if(!/[^0-9!##$%^&*()+=;./{}|:<>?\[\]\\\'\"]{7}/.test(val)) {
// handle invalid value
}
This tests that the string is 7 characters in length and does not contain any character within the brackets after the "^" (also some characters are escaped with a "\").
You can test in console:
/[^0-9!##$%^&*()+=;./{}|:<>?\[\]\\\'\"]{7}/.test('adfFDKZ'); // returns true
/[^0-9!##$%^&*()+=;./{}|:<>?\[\]\\\'\"]{7}/.test('adf(DKZ'); // returns false
Try this:
/*
* This regex matches all the invalid characters. I escaped the
* special characters.
*/
var regex = /.*[0-9!##\$%\^&\*\(\)\+=\[\]\\';\./\{\}\|":\<\>\?]+.*/;
var text = document.getElementById("TextBox1").value;
/* Test for match...much faster than a for-loop under any circumstances */
if (text.matches(regex)) {
alert("Invalid characters present. Please correct the input");
return false;
}
/* split on delimiter */
var err = $.grep(val.split(','), function(a) { return a.length != 7; });
if (err.length) {
alert("All entries must be seven (7) characters in length. Please correct the following entries: \n" + err);
return false;
}
Please tell me if there are any bugs in this. Also, the only real way to test for this in one step is to set up an enormously long regex. Also, with only one check, it would make it a little harder to guide the user to make the right correction. I will mention that.

Categories

Resources