I'm trying to find the substring in string using O(N) complexity. The following is the code I have written. It returns undefined and I don't know why. Please let me know what is going wrong in my code.
let omg = "omg";
let omgi = "omjiklmonomgib";
function stringSearch(smallString, bigString) {
let left = 0;
let right = left+(smallString.length - 1);
while (left > right) {
if (smallString[left] === bigString[left]) {
if (smallString[right] === bigString[right]) {
left++;
right--;
if (left === right) {
if (smallString[right] === bigString[left]) {
return true;
} else if (right === left + 1) {
if (smallString[right] === bigString[right] && smallString[left] === bigString[left]) {
return true;
} else {
left = right + 1;
right = left + (smallString.length - 1);
}
}
}
}
}
}
}
console.log(stringSearch(omg, omgi)); //Undefined
From what I understand, you're just remaking String.prototype.match. Try checking this out, as it would probably be an easier way to do what you're saying. Sorry, missed the O(N) complexity part.
If you really wanna make a custom one, I can give you some tips.
First of all, you should have a "cache" variable (one for all, not left and right) and another variable which will be found (it'll be a boolean, so set it to false). The "cache" variable will store the text, and the other one will store whether you found the smallString.
Basically, you loop through every character and store however long smallString characters in the "cache" variable. Once the "cache" variable is the same length as smallString, run an if statement on it. If it's not equal to the smallString, remove the first character of the "cache". Next iteration in the loop, it'll add another character. You then do the same as before, run an if statement and if it's not equal remove the first character and continue the loop until you find it, or the string ends. If you found it, set the boolean to true.
Something like this:
function stringSearch(smallString, bigString, caseSensitive=true) {
if(!caseSensitive) { // if caseSensitive is false, make everything lower case
smallString = smallString.toLowerCase();
bigString = bigString.toLowerCase();
}
let cache = ""; // string cache
let found = false; // result
for(i=0;i<bigString.length;i++) { // loop through every character in bigString
cache+=bigString[i]; // add the current character to the cache
if(cache.length == smallString.length) { // check if the cache's length is the same as the smallString's length
if(cache == smallString) { // check if the cache is equal to the smallString
found = true; // set found to true
break; // break the loop (stop it from going on)
} else {
cache = cache.substring(1); // set cache to itself but remove the first character
}
}
}
return found; // return result
}
// example:
console.log("String 'hello, world' has 'hello': "+stringSearch("hello", "hello, world"));
console.log("String 'HELLO WORLD' has 'hello': "+stringSearch("hello", "HELLO WORLD"));
console.log("String 'HELLO WORLD' has 'hello' (not case sensitive): "+stringSearch("hello", "HELLO WORLD", false));
console.log("String 'hey hi hello WORLD' has 'hello': "+stringSearch("hello", "hey hi hello WORLD"));
console.log("String 'hey hi hello' has 'hello': "+stringSearch("hello", "hey hi hello"));
console.log("String 'hey lol o' has 'hello': "+stringSearch("hello", "hey lol o"));
I see a couple of issues. First, the code doesn't have a return statement for every logical branch. I mean that, for every condition in an if, then, else statement, the code should have a return statement or some sort of control-flow statement (e.g. a recursive call or a continue) that will eventually lead to a return statement.
The second issue is the while loop. Supposedly, right should be greater than left from the beginning of the function (unless the length of smallString is 0) because right is the length of smallerString minus 1. The while condition is left > right, so nothing inside of the while will be executed unless smallString has a negative length (which it doesn't).
By the way, if you want to check the entire bigString, you will need to iterate over bigString, not smallString. If you are only checking smallString.length characters, you won't be checking the entire bigString. Figuring out how to write this function is a good exercise, so I will leave the writing of it to you and refrain from providing an implementation myself. Keep up the good work!
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();
}
}
function palindrome(str) {
var len = str.length;
for ( var i = 0; i < Math.floor(len/2); i++ ) {
if (str[i] !== str[len - 1 - i]) {
return false;
}
}
return true;
}
If I replace !== and false inside the for loop with === and true and outside true with false the function does not work as expected Can someone tell me why?
Let me illustrate it on an example
Let's say we have covered box full of white and black marbles. And a statement: "The box contains only white marbles". How can you be sure, that the statement is true? Well... you pick marbles one by one (in a for loop) and check if they are all white. If you'll find one that is not (!==), you know that the statement is false. And your function is doing this.
The way you want it - with switched operator and true value - it'll go like this: "I will pick one marble and if it IS white (===), I know the statement is true." Now... is it really true? No it isn't.
Got it? (In your case you are not checking the black and white marbles but whether the characters are equal or not)
Answer #Wiktor Stribiżew suggested:
function myValidate(word) {
return (word.length === 1 || /[^A-Z]/i.test(word)) ? true : false;
}
Hello during the creation of an array I have a function that will not allow words with certain characters etc to be added to the array
function myValidate(word) {
// No one letter words
if (word.length === 1) {
return true;
}
if (word.indexOf('^') > -1 || word.indexOf('$') > -1) {
return true;
}
return false;
}
It seems like not the proper way of going about this and ive been looking into a regex that would handle it but have not been successful implementing it, tried numerous efforts like:
if (word.match('/[^A-Za-z]+/g') ) {
return true;
}
can some one shed some light on the proper way of handling this?
I suggest using a simpler solution:
function myValidate(word) {
return (word.length === 1 || /[^A-Z]/i.test(word)) ? false : true;
}
var words = ["Fat", "Gnat", "x3-2741996", "1996", "user[50]", "definitions(edit)", "synopsis)"];
document.body.innerHTML = JSON.stringify(words.filter(x => myValidate(x)));
Where:
word.length === 1 checks for the string length
/[^A-Z]/i.test(word) checks if there is a non-ASCII-letter symbol in the string
If any of the above condition is met, the word is taken out of the array. The rest remains.
EDIT: using test instead of match
You want to use test() because it returns a bool telling you if you match the regex or not. The match(), instead, always returns the matched elements. Those may be cast to true by coercion. This is not what you want.
To sum it all up you can just use this one-liner (no if needed and no quotes either, cannot get any simpler):
return word.test(/^[a-zA-Z][a-zA-Z]+$/); // two letter words
You should whitelist characters instead of blacklisting. That's one of the principles in security. In your case, don't tell what is wrong, but tell what is right:
if (word.test('/^[a-zA-Z]+$/')) { // two letter words
return false;
}
This will return false for all words that contain ONLY [a-zA-Z] characters. I guess this is what you want.
Your regex, instead, looked for illegal characters by negating the character group with the leading ^.
Two recommendations:
Just use regex in a positive way (without negation) and it'll be a lot easier to understand.
Also, validation functions normally return true for good data and false for bad data.
It is more readable this way:
if (validate(data))
{
// that's some good data we have here!
}
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.
this might be a very basic question, but I would like to know how I can find out if .html() has a particular value (in this case a string). An example:
<p id="text">Hello this is a long text with numbers like 01234567</p>
and I would like to ask
var $text = $('#text');
if ($text.html() == '01234567')
of course this would not work. But how can I enhance another method to .html() that asks
if($text.html().contains() == '01234567');
Important to say is, that in my case I definitely will search for things who are seperated with a space, not like withnumberslike01234567 but indeed it would be interesting if that would work as well.
Thanks in advance!
(' ' + document.getElementById('text').textContent + ' ').indexOf(' 01234567 ') != -1
Fixes problem with the text at the beginning, doesn't abuse regex, and hooray for vanilla.js!
You can use indexOf:
var text = $('#text').html();
if(text.indexOf(" 01234567") != -1) {
// your logic
}
Your HTML might start with 01234567, though; in that case, you can do this:
if((' ' + text).indexOf(" 01234567") != -1) {
// your logic
}
Thanks, bjb568 and Felix Kling.
As I understand from OP, these are the test cases:
hello12348hello // false
hello 1234hello // false
hello012348 hello // false
hello 1234 hello // TRUE
1234hello // false
hello1234 // false
1234 hello // TRUE
hello 1234 // TRUE
// false
1234 // TRUE
1234 // TRUE
** Changing "" by any other white-space character (e.g. \t, \n, ...) should give same results.
As OP said:
for things who are separated with a space, not like withnumberslike01234567
So, hello 01234567withnumberslike is also wrong!!!
Creating the function:
function contains(value, searchString){
// option 1: splitting and finding a word separated by white spaces
var words = value.split(/\s+/g);
for (var i = 0; i < words.length; i++){
if (words[i] === searchString){
return true;
}
}
return false;
// option 1a: for IE9+
return value.split(/\s+/g).indexOf(searchString) > -1;
// option 2: using RegEx
return (new RegExp("\\b" + searchString + "\\b")).test(value);
return (new RegExp("(^|\\s)" + searchString + "($|\\s)")).test(value); // this also works
// option 3: Hardcoded RegEx
return /\b1234\b/.test(value);
}
See case tests here in jsFiddle
It will also accept tabs as well as whitespaces..
NOTE I wouldn't worry about using RegEx, it isn't fast as indexOf, but it stills really fast. It shouldn't be an issue, unless you iterate millions of times. If it would be the case, perhaps you'll need to rethink your approach because probably something is wrong..
I would say to you think about compatibility, there is a lot of users still using IE8, IE7, even IE6 (almost 10% right now - April, 2014). -- No longer an issue in 2016..
Also, it's preferred to maintain code standards.
Since, you are using jQuery you can use too .text() to find string:
var element = $(this);
var elementText = element.text();
if (contains(elementText, "1234"){
element.text(elementText.replace("1234", "$ 1234.00"))
.addClass("matchedString");
$('#otherElement').text("matched: 1234");
}
Thanks to #Karl-AndréGagnon for the tips.
\b: any boundary word (or start/end of the string)
^: start of the string
\s: Any whitespace character
$: end of the string
http://rubular.com/r/Ul6Ci4pcCf
You can use the String.indexOf method in JavaScript to determine whether or not one string is contained in another. If the string passed into indexOf is not in the string, then -1 is returned. This is the behavior you should utilize.
If ($test.html().indexOf("1234567890") != -1)
//Do Something
if($text.html().indexOf('01234567') != -1) {
}
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/indexOf