CodeAcademy: JavaScript Second "For" Loop in Text search - javascript

So, it's about making a text search. I was told to start with 3 var: text, myNames, and hits. I was supposed to push my name that I had placed within the 'text' to 'hits'.
I understand the first for loop, which is to search through the text with:
for ( i = 0 ; i <= text.length ; i++ ) {
if(text[i] === "S");
}
But then, I don't really understand the instruction,"So if your name is 11 letters long, your loop should add 11 characters to hits if it ever sees the first letter of myName in text."
So I checked the hint, which got me more confused, it says: "Your loop should stop when it hits the value of the first iterator (say, i) plus the length of your myName variable."
What? I tried to follow the hint, and it seemed true because it allowed me to go to the next lesson. But the console merely logged 'S'
var text ="A string containing some text. \ I place my name here, Smit, \ a couple of times, so \ I need at least one more, \ Smit is it enough? \ Or we need more? \ Smit Smit Smit!";
var myName = "Smit";
var hits = [];
for ( i = 0 ; i <= text.length ; i++ ) {
if(text[i] === "S"); {
for ( j = i ; j <= (text.length + myName.length); j++ ) {
hits.push("S");}
}
}
enter image description here
But I don't understand why did I need to use "(text.length + myName.length)"?

I think you misunderstood the instruction.
The instruction "So if your name is 11 letters long, your loop should add 11 characters to hits if it ever sees the first letter of myName in text" means the following code.
for (j = i; j < (i + myName.length); j++)
You should use i + myName.length instead of text.length + myName.length. If the first character of myName lies on the i-th position, then the final character of myName will lie on the (i + myName.length - 1)-th position. You can write a few strings and check it yourself.
It seems that codecademy's checking is not very strict, so it doesn't mean that your code is 100% correct even if it allows you to move to the next section.
But I don't quite understand why the instruction says "add 11 characters to hits if it ever sees the first letter of myName in text". Shouldn't we check the whole name and then decide to push it into hit or not?

Related

Replace(), replacing a value not specified

I've been learning Javascript via FCC for 6 weeks now, and decided to spend a week learning, and playing around with methods to get really close and comfortable with using them in loops, and with statements. Just playing around with strings, I was trying to replace the string "I like milk", with "I like silk". My code accomplishes this, and I understand it, but I decided to add an || operator, and the outcome bewilders me. If i say if(x[i]=== 'm' || x[i] === 'I', it doesn't replace 'I', but replaces 'm' If i leave it the way it is in my original code, it produces the string 'I lise milk', even though 'k' was never mentioned. What is going on?
let x = 'Hello I like milk';
let y = '';
for(let i = 0; i < x.length; i++) {
if(x[i] === 'm' || 'I') {
y = x.replace(x[i], 's' )
}
}
console.log(y)
ok, there are a couple of gotchas that make your code interesting
x[i] === 'm' || 'I' is always true, because 'I' is truthy - if that's not clear, you can test this with if ('I') { console.log('Merry Christmas') }
your loop produces a new string with one replacement every loop, i.e. it produces the same result as no loop and
y = x.replace(x[x.length - 1], 's')
the replace will replace the first matching character - the last character in your string is k, so the replace will change the first matching k to s
so, the result of your code is
'Hello I lise milk'
Hope that helps clear things up!
btw, the easiest way to replace all ms and Is with an s:
console.log(x.replace(/[mI]/g, 's'))
So you basically are running into an issue with the way your code is flowing.
Let's start with issue one: Why is the m being overwritten but not the y?
So basically you have var x and y that you are updating throughout your loop.
The way your code works is basically: If the letter equals m or I replace it with an s.
The problem you are going to have here however is you never store the value that has since been updated. So when it loops again it is taking the default value of x (which hasn't actually been updated) and is writing it to y. This is going to overwrite the value each time.
We can demo this by simply logging inside of the loop.
let x = 'Hello I like milk';
let y = '';
for(let i = 0; i < x.length; i++) {
if(x[i] === 'm' || x[i] === 'I') {
y = x.replace(x[i], 's' )
console.log(y)
}
}
So if you run the above, you will see two lines being outputted.
Hello s like milk
Hello I like milk
The m is the only one being printed however because the log is after the final update.
So the next issue: Why is replacing that k?
As another commented posted, "I" is always going to be truthy. Truthy basically means the value isn't null, undefined, 0 etc. "I" has a value which means it will always be true.
So is the K being replaced? Well if we run that code, the final letter being checked and replaced is a k. .replace only replaces (by default) the first instance of that letter it comes across. In your case, that K is the first K that is seen.
So to fix it, don't check on "I" check: x[i] === "I"
EDIT: To answer your question about why the last letter matters
So the last letter matters here because you have what basically equates to:
if("I"){}
So that above snippet, contrary to what you may think actually means if this has a value, which because I is a valid character, will always report true. So for your loop, everything is actually being checked, regardless of the character because I is always going to be true.
Here is an easy way to check it:
Lets say I have an array of integers from 1 to 10. I have (pseudo code):
if array value < 10 OR I
Print that value
With the way you are currently thinking, you would expect it to stop printing if the values are less than 10 right? Nope! Once again because I is always truthy and we are saying OR the value I (true) so we are going to always get a print!
Code example:
var x = [0, 1, 2, 3, 4 , 50, 60, 70, 80];
for(var i = 0; i < x.length; i++){
if(x[i] < 10 || "I") {
console.log(x[i]);
}
}
See? Everything prints out! Even though half of the values are over 10 they are still printing out. Why? Because "I" is always true! Now let's edit that a bit to make it a bit more strict in checking:
let x = [0, 1, 2, 3, 4 , 50, 60, 70, 80];
for(var i = 0; i < x.length; i++){
if(x[i] < 10 || x[i] === "I") {
console.log(x[i]);
}
}
See? Much better :)

How do I grab user input after an # symbol and before a space?

I want to grab the user input from an input tag including everything after the # symbol and up to a space if the space exists. For example:
If the user input is "hello#yourname"
I want to grab "yourname"
If the user input is "hello#yourname hisname"
I want to grab "yourname" because it is after the # symbol and ends at the space.
I have some code written that attempts to grab the user input based on these rules, but there is a bug present that I can't figure out how to fix. Right now if I type "hello#yourname hisname"
My code will return "yourname hisn"
I don't know why the space and four characters "hisn" are being returned. Please help me figure out where the bug is.
Here is my function which performs the user input extraction.
handleSearch(event) {
let rawName, nameToSearch;
rawName = event.target.value.toLowerCase();
if (rawName.indexOf('#') >= 0 && rawName.indexOf(' ') >= 0) {
nameToSearch = rawName.substr(rawName.indexOf('#') + 1, rawName.indexOf(' ') - 1);
} else if (rawName.indexOf('#') >= 0 && rawName.indexOf(' ') < 0) {
nameToSearch = rawName.substr(rawName.indexOf('#') + 1);
} else {
nameToSearch = '';
}
return nameToSearch;
}
Working example:
handleSearch(event) {
let rawName = event.target.value.toLowerCase();
if (rawName.indexOf("#") === -1) {
return '';
}
return (rawName.split("#")[1].split(" "))[0];
}
You have to handle a lack of "#", but you don't need to handle the case where there is a space or not after the "#". The split function will still behave correctly in either of those scenarios.
Edit: The specific reason why OP's code doesn't work is because the substr method's second argument is not the end index, but the number of characters to return after the start index. You can use the similar SUBSTRING method instead of SUBSTR to make this easier. Change the line after the first if statement as follows:
nameToSearch = rawName.substring(rawName.indexOf('#') + 1, rawName.indexOf(' '));
const testCases = [
"hello#yourname",
"hello#yourname hisname"
];
for (let test of testCases) {
let re = /#(.*?)(?:\s|$)/g;
let result = re.exec(test);
console.log(result[1]);
}
Use regex instead if you know how the string will be created.
You could do something like this--
var string = "me#somename yourname";
var parts = string.split("#");
var parts2 = parts[1];
var yourPart = parts2.split(" ");
console.log(yourPart[0]);
NOTE:
I am suggesting it just because you know your string structure.
Suggestion
For your Piece of code I think you have some white space after hisn that is why it is returning this output. Try to replace all the white spaces with some character see if you are getting any white space after hisn.
I'm not sure of the language your code is in (there are several it 'could be', probably Javascript), but in most languages (including Javascript) a substring function 'starts at' the position of the first parameter, and then 'ends at' that position plus the second parameter. So when your second parameter is 'the position of the first space - 1', you can substitute 'the position of the first space - 1' with the number 13. Thus, you're saying 'get a substring by starting one after the position of the first # character i.e. position 6 in a zero-based system. Then return me the next 13 characters.'
In other words, you seem to be trying to say 'give me the characters between position 6 and position 12 (inclusive)', but you're really saying 'give me the characters between position 6 and position 18 (inclusive)'.
This is
y o u r n a m e h i s n
1 2 3 4 5 6 7 8 9 10 11 12 13
(For some reason I can't get my spaces and newlines to get preserved in this answer; but if you count the letters in 'yourname hisn' it should make sense :) )
This is why you could use Neophyte's code so long as you can presume what the string would be. To expand on Neophyte's answer, here's the code I would use (in the true branch of the conditional - you could also probably rename the variables based on this logic, etc.):
nameToSearch = rawName.substr(rawName.indexOf('#') + 1;
var nameFromNameToSearch = nameToSearch.substr(nameToSearch.indexof(' ') - 1;
nameFromNameToSearch would contain the string you're looking for. I haven't completely tested this code, but I hope it 'conceptually' gives you the answer you're looking for. Also, 'conceptually', it should work whether there are more than one '#' sign, etc.
P.S. In that first 'rawName.substr' I'm not giving a second parameter, which in Javascript et al. effectively says 'start at the first position and give me every character up to the end of the string'.

How to find total possible values from length and characters?

I'm totally not a Math whiz kid here, but have put together a function with the great help of StackOverflow (and a lot of trial and error) that generates a random serial number from a Formula, group of Letters/Numbers, and array (so as to not duplicate values).
So, my current formula is as follows:
$.extend({
generateSerial: function(formula, chrs, checks) {
var formula = formula && formula != "" ? formula : 'XXX-XXX-XXX-XXX-XXX', // Default Formula to use, should change to what's most commonly used!
chrs = chrs && chrs != "" ? chrs : "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", // Default characters to randomize, if not defined!
len = (formula.match(/X/g) || []).length,
indices = [],
rand;
// Get all "-" char indexes
for(var i=0; i < formula.length; i++) {
if (formula[i] === "-") indices.push(i);
}
do {
rand = Array(len).join().split(',').map(function() {
return chrs.charAt(Math.floor(Math.random() * chrs.length));
}).join('');
// Rebuild string!
if (indices && indices.length > 0)
{
for(var x=0; x < indices.length; x++)
rand = rand.insert(indices[x], '-');
}
} while (checks && $.inArray(rand, checks) !== -1);
return rand;
}
});
Ok, so, what I need to be able to do is to find total possible values and make sure that it is possible to generate a unique serial number before actually doing so.
For example:
var num = $.generateSerial('XX', 'AB', new Array('AB', 'BA', 'AA', 'BB'));
This will cause the code to do an infinite loop, since there are no more possibilties here, other than the ones being excluded from the extension. So this will cause browser to crash. What I need to be able to do here is to be able to get the number of possible unique values here and if it is greater than 0, continue, otherwise, don't continue, maybe an alert for an error would be fine.
Also, keep in mind, could also do this in a loop so as to not repeat serials already generated:
var currSerials = [];
for (var x = 0; x < 5; x++)
{
var output = $.generateSerial('XXX-XXX-XXX', '0123456789', currSerials);
currSerials.push(output);
}
But the important thing here, is how to get total possible unique values from within the generateSerial function itself? We have the length, characters, and exclusions array also in here (checks). This would seem more like a math question, and I'm not expert in Math. Could use some help here.
Thanks guys :)
Here is a jsFiddle of it working nicely because there are more possible choices than 16: http://jsfiddle.net/qpw66bwb/1/
And here is a jsFiddle of the problem I am facing: Just click the "Generate Serials" button to see the problem (it continuously loops, never finishes), it wants to create 16 serials, but 16 possible choices are not even possible with 2 characters and only using A and B characters: http://jsfiddle.net/qpw66bwb/2/
I need to catch the loop here and exit out of it, if it is not able to generate a random number somehow. But how?
The number of possible serials is len * chrs.length, assuming all the characters in chrs are different. The serial contains len characters to fill in randomly, and chrs.length is the number of possible characters in each position of that.

Check letter in word [hangman]

I'm new in programming.I have to write hangman game i javascript and have a big problem :D This is my function.The problem is : when user enter right letter it is appears in Wrong Guess too and I don't know why.
function IsLetterInWord(letter)
{
for(i = 0; i< Word.length; i++)
{
if(letter == Word[i])
{
secretword[i] == letter;
RightGuess += letter;
var el = document.getElementById("right");
el.innerHTML = "Your right guesses are:" + RightGuess;
win();
}
}
if (letter != Word[i]){
WrongGuess += letter;
var e = document.getElementById("wrong");
e.innerHTML = "Your wrong guesses are:" + WrongGuess;
}
}
You should use of the indexOf() method.The method returns the first index at which a given element can be found in the array, or -1 if it is not present.
Link to docs
"string".indexOf("r") > -1;
You also need to use single equals on line #7:
secretword[i] = letter; // instead of ==
You've got a problem with your logic: you're treating a letter as wrong if it doesn't match every letter in the target, whereas it should only count as wrong if it doesn't match any letter. Also, a wrong letter should only count once, whereas a right letter should count for each match.
Just as a matter of developing good habits (though it makes no real difference in something as small as this), try not to repeat DOM operations unnecessarily since they're fairly slow. Things like
var el = document.getElementById("right");
should be done before a loop rather than inside it (the identity of the element plainly isn't going to change) and things like
el.innerHTML = "Your right guesses are:" + RightGuess;
should only be done after the loop finishes (the browser has to redraw part of the screen each time it's called).

Codecademy lesson troubles

I am learning JavaScript through Codecademy, but I have an issue. The code below is supposed to search through the text variable for my name in the myName variable and then push all of the individual letters to the hits array. The code that I have written is not correct but Codecademy says that it is correct and is going to let me move on in the lesson.
I have been trying to solve the issue that I am having with no luck. The problem is that when I run the hits.push(text); line it will output the entire variable but I have tried hits.push(text[i]); and get undefined for the result. Can someone please help me understand where I have made the mistake?
/*jshint multistr:true */
var text = "XsddfasASSFABrandonSFsdfdasBrandonsddfadfaBrandon";
var myName = "Brandon";
var hits = [];
for (i=0; i<=text.length;i++){
if (text[i]===myName[i]){
for(var x=i; x<i+myName.length;x++){
hits.push(text);
}
}
}
if (hits.length===0){
console.log("Your name wasn't found!");
} else {
console.log(hits);
}
The best way I can think to explain your mistake is simply by walking through a bit of the logic of what you have written.
for (i=0; i<=text.length;i++){
Your for loop will iterate i for as many characters as there are in your text variable, so: 49 times.
if (text[i]===myName[i]){
The first run through your for loop, where i=0, you are checking to see if text[0] is strictly equal to myName[0]. text[0] = X and myName[0] = B. The strictly equals condition is not met, so the loop proceeds to increment i repeat: text[1] = s and myName[1] = r. This continues 47 more times, and the condition is never met. myName[i] is undefined after the first 7 loops.
Normally you would do this kind of thing using indexOf, match, search, substr or substring, which are all string methods.
However for the purpose of this exercise you can do:
var text = "XsddfasASSFABrandonSFsdfdasBrandonsddfadfaBrandon";
var myName = "Brandon";
var hits = [],
namePosition = 0;
for (var i = 0; i < text.length; i++) {
if (text[i] === myName[namePosition]) {
hits.push(text[i]);
namePosition ++;
if (hits.length === myName.length) {
break;
}
}
else {
namePosition = 0;
hits = [];
}
}
if (hits.length === 0) {
console.log("Your name wasn't found!");
} else {
console.log(hits);
}​
(See it working at http://jsfiddle.net/wCWxr/1/). The problems with your original code include:
you try to compare text[i] to myName[i] but the indices of the two strings won't match up.
you try to push the entire string text into hits instead of one character at a time
your logic doesn't deal with the possibility that the beginning but not the end of myName is in text, e.g. if text was aerwerBrasdfsgars
My suggestion fixes this by recording (with namePosition) what position we are currently at within the string myName, and incrementing that when we find a character in text that matches the relevant character in myName. If the characters do not match then it's not a true hit, so we reset hits = [] and namePosition = 0. If the characters all match then hits eventually reaches the length of myName and so we break out of the loop.
If you are trying to find if myName is in text here is what you do:
RegExp:
var pattern = new RegExp(myName);
if (pattern.test(text)){
console.log(myName);
}else {
console.log("Your name wasn't found!");
}
indexOf:
if (text.indexOf(myName) != -1){
console.log(myName);
}else {
console.log("Your name wasn't found!");
}
if (text[i]===myName[i]){
this line should create an error, because myName[i] is not the first letter of myName.
if (text[i]===myName[0]){
Change to this line should work.

Categories