Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

JavaScript JavaScript Loops, Arrays and Objects Tracking Data Using Objects The Student Record Search Challenge Solution

Sam Muir
Sam Muir
16,745 Points

Having an issue - can't find real records :s

So I was trying to do the additional question, to check for multiple records... I wanted to add in a proper alert message when no student record matched the 'search'

I've used the following code, and now it's not finding any records. :S

Not sure what I've done exactly... lol.

var message = '';
var student;
var search;


// Function to create message to print
function print(message) {
  var outputDiv = document.getElementById('output');
  outputDiv.innerHTML = message;
}

// Function which gets Student Report
function getStudentReport ( student ) {
 var report = '<h2>Student: ' + student.name + '</h2>';
  report += '<p>Track: ' + student.track + '</p>';
  report += '<p>Points: ' + student.points + '</p>';
  report += '<p>Achievements: ' + student.achievements + '</p>';
  return report;
}

// Loop that runs while true
while (true) {
 search = prompt('Search student records: type a name [Jody] (or type "quit" to end)'); 
  if (search.toLowerCase() === 'quit' || search.toLowerCase() === null) {
   break; 
  }    
  for (var i = 0; i < students.length; i += 1) {
    student = students[i];
    if ((student.name.toLowerCase()) === search.toLowerCase()) {
      message = getStudentReport( student );
      print (message);
    } else {
        alert('Sorry, no student matches the name ' + search);
        i = students.length +1;
    }
  }  
}

4 Answers

huckleberry
huckleberry
14,636 Points

Howdy Sam!

I can't see your array of students but I realize that it's in a different script but either way, here's your main problem as well as a couple of other tidbits.

I made a codepen but first I'll comment on your code in snippets so I can clear up your direct confusion before you dive into what I wrote. It's not complicated at all, just don't want to muddy the water with "here look at this" first.

//We'll get right to the heart of the matter.

/*++++++++++++++++++
the problem lies in here
+++++++++++++++++++*/
while (true) {
 search = prompt('Search student records: type a name [Jody] (or type "quit" to end)'); 
/*1. You never need to .toLowerCase() null. Null does not have the ability to be lower-cased
(it already is but that's not the point lol) or uppercased. It just is null. */
  if (search.toLowerCase() === 'quit' || search.toLowerCase() === null) {
   break; //lovely
  } 
  for (var i = 0; i < students.length; i += 1) { //good ... expression is perfect
    student = students[i]; //gooood
    if ((student.name.toLowerCase()) === search.toLowerCase()) { /*2. ok, here you wrapped 
student.name.toLowerCase() in parenthesis. Um... why? lol. Was it just a mistake or did you have 
a reason in mind for doing it? You know, maybe you thought you had to? Just curious*/
      message = getStudentReport( student );
      print (message);
    } else { //3. HERE is the kicker. This will ALWAYS fire with the way you have it written. Always. Even if we fix the next problem.
        alert('Sorry, no student matches the name ' + search);
        i = students.length +1; //DING DING DING. You just the iteration variable i to the exact length of the students array PLUS 1. When this gets back to the beginning of the for loop it's going to increment one more time and will automatically evaluate to false. This right here is forcing your for loop to run exactly ONE time if you search for anything other than the name of the very first student in the array.
    }
  }  

Ok, so ...

Unneccesary method call on null

/*1. You never need to .toLowerCase() null. Null does not have the ability to be lower-cased
(it already is but that's not the point lol) or uppercased. It just is null. */
  if (search.toLowerCase() === 'quit' || search.toLowerCase() === null) {
   break; //lovely
  } 
  1. null is and of itself an object. Everything is an object in JavaScript. Just like you cannot do this
var samMuir = {
    firstName:  "Sam",
    lastName: "Muir"
}

var samLowerCase = samMuir.toLowerCase();

You cannot toLowerCase() null. Go ahead and try it.

  • Open up a new tab in chrome or whatever you use (hopefully chrome or FF lol) and
  • In the address bar type about:blank and hit enter
  • Type CTRL + SHIFT + J to open the console and
  • Create a var named a and set its value to null
  • then try and console.log(a.toLowerCase());

See?

It doesn't necessarily break your code, but it's unnecessary.

Extra Parenthesis

 if ((student.name.toLowerCase()) === search.toLowerCase()) { /*2. ok, here you wrapped 
student.name.toLowerCase() in parenthesis. Um... why? lol. Was it just a mistake or did you have 
a reason in mind for doing it? You know, maybe you thought you had to? Just curious*/
      message = getStudentReport( student );
      print (message);
    }

2. I'm just really curious why you did that. If it was just an oversight or if you thought you had to for some reason. I know I've made plenty of those "gee, I dunno, I just assumed I had to do it like that!" blunders but seeing as how you did not do that in the previous conditional, I'm guessing it was just an oversight. Still, had to ask.

else { //3. HERE is the kicker. This will ALWAYS fire with the way you have it written. Always. Even if we fix the next problem.
        alert('Sorry, no student matches the name ' + search);
        i = students.length +1; //DING DING DING. You just the iteration variable i to the exact length of the students array PLUS 1. When this gets back to the beginning of the for loop it's going to increment one more time and will automatically evaluate to false. This right here is forcing your for loop to run exactly ONE time if you search for anything other than the name of the very first student in the array.
    }

3. AHA!! There it is... ok, so I commented it but just gonna go over this here in more depth. Think about this for loop as a whole here.

for (var i = 0; i < students.length; i += 1) { 
    student = students[i]; 

    if ((student.name.toLowerCase()) === search.toLowerCase()) { 

      message = getStudentReport( student );
      print (message);
    } else { 
        alert('Sorry, no student matches the name ' + search);
        i = students.length +1; 
    }

Let's say you have 3 students, k? Bill, Jill, and Will in that order within the array.

The user gets the prompt and they search for Jill. What happen?

  • It immediately stores the first object of the array into student. So far so good.
  • It goes in and checks to see if the name property of that object is the same as the search value.
  • It returns false because the user searched for Jill.
  • It skips everything in the if block
  • It runs into the else block
  • It pops up the alert
  • it sets the value of i to 4 because you just told it to set its value to the length of the array (which is 3) plus an additional one just for good measure lol
  • It goes back to the beginning expression and then increments one more time
  • It goes to the actual condition of i < students.length and evaluates to false because i, after one loop, is now equal to students.length + 2 lol

See?

That's why you're not getting any results returned. At MOST you'll get one result returned IF you search for the name of the very first student object.

Let's walk through that... (reposting the code so you don't have to scroll)/ Remember, we have Bill, Jill, and Will.

for (var i = 0; i < students.length; i += 1) { 
    student = students[i]; 

    if ((student.name.toLowerCase()) === search.toLowerCase()) { 

      message = getStudentReport( student );
      print (message);
    } else { 
        alert('Sorry, no student matches the name ' + search);
        i = students.length +1; 
    }

So, once again...

  • It immediately stores the first object of the array into student. So far so good.
  • It goes in and checks to see if the name property of that object is the same as the search value.
  • It returns true this time because the user searched for Bill and hey, that's the value of the first object's name property.
  • It runs the if block, printing out your message just as planned
  • It skips over the else
  • It goes back to the beginning expression
  • it increments appropriately and sets the value of i to 1
  • It goes to the actual condition of i < students.length and evaluates to true because 1 is less than 3 obviously.
  • It then continues, evaluates to false because the second student is Jill, it jumps into the else statement and then the loop is done because once again, i gets set to 4 which evaluates to false when it goes to check the beginning of the for loop again.

So even if there was another Bill later on down the array, it would never reach it because of that ooooone screwy assignment. Aint coding fun! :laughing:

So yeah, that's it!

I'm not entirely sure what you were trying to do there with changing i's value in the else block, but that's the cause of all the trouble :)

Here's a codepen of my variation with some additional things factored in

http://codepen.io/huckleberry/pen/VazpvV

Here is the code without the extensive comments. I left the comments on the codepen.

var message = "";
var student;
var search;
var students = [
  {
    name:"Mark",
    track: "Front End Web Development",
    points: 1345,
    achievements: "Butter Chops"
  },
  {
    name: "John",
    track: "IOS Fundamentals",
    points: 444,
    achievements: "Noob"
  },
  {
    name: "Bill",
    track: "Web Design",
    points: 45,
    achievements: "Tag you're it!"
  },
  {
    name: "Mark",
    track: "PHP Development",
    points: 687,
    achievements: "Space Cougar"
  },
  {
    name: "Susan",
    track: "Full Stack JavaScript",
    points: 1687,
    achievements: "Swiggity Swooty"    
  },
  {
    name: "Ling",
    track: "Rails Development",
    points: 3857,
    achievements: "Snuffleupagus"
  },
  {
    name: "Bill",
    track: "IOS Development",
    points: 2361,
    achievements: "Count Chocula"
  }
];

// Function to create message to print
function print(message) {
  var outputDiv = document.getElementById('output');
  outputDiv.innerHTML = message;
}

// Function which gets Student Report
function getStudentReport ( student ) {
 var report = '<div><h2>Student: ' + student.name + '</h2>';
  report += '<p>Track: ' + student.track + '</p>';
  report += '<p>Points: ' + student.points + '</p>';
  report += '<p>Achievements: ' + student.achievements + '</p></div>';
  return report;
}

/*fun little thing for when your search returns no results.*/
function noStudent(){
  var denied = "<h1><span id='warning'>No student for you!</span></h1>";
  print(denied);
}

// iterate over the array looking for the query string
while (true) {
   //initialize flag
    var found = 0; 

    search = prompt('Search student records: type a name [Jody] (or type "quit" to end)');


    if (search === null || search.toLowerCase() === 'quit' ) {
        break; 
    } else if (search === ""){ // restarts the loop if the user just hits enter or clicks OK without entering anything. Without it, you'll get the "sorry that student doesn't exist" alert.
        continue; //continue in a while loop skips everything below it and goes back to the condition at the beginning
    }

    search = search.toLowerCase(); 
    message = ""; 

    for (var i = 0; i < students.length; i += 1) {
        student = students[i];       

        if (student.name.toLowerCase() === search) {
            message += getStudentReport(student);
            found = 1; //flag set to true
        }
    } 
    if (!found){
        noStudent();
        alert("sorry, that student doesn't exist!");
    } else {      
        print(message);
    }
}

If you have any questions, let me know!!

Cheers,

Huck - :sunglasses:

Sam Muir
Sam Muir
16,745 Points

Thanks Huck,

Yup, I noticed the loop was the main issue. Good to know I was on the right track :).

Whoops - looks like adding the .toLowerCase() against null was an oversight.

Haha I think it was just late in the afternoon and I added the extra parenthesis without thinking too much tbh.

Ah, of course - I don't need to add +1 to the length of the Array. Haha. I was originally trying to create a way to exit the else loop, but still let the original While Loop run, but it just breaks the code, so I'll remove it.

Thanks again for your detailed answer - it really helps explain everything,

Cheers,

Sam

Sandra Qu
Sandra Qu
577 Points

Sam, I think you have two issues with your code: 1. The "break" in the conditional is also stopping the while loop, and 2. The while loop is infinite because it is always true.

The while loop is not necessary, but if you feel you need it, you ought to set a var to true, and toggle it false when you are done looping through students.length in the for loop. A loop within a loop is something to be avoided however.

I tried your code here: http://codepen.io/sandraqu/pen/VazmQK/?editors=0010

Take a look at my modifications. You can try with user name "Bem"

huckleberry
huckleberry
14,636 Points

Hiya Sandra Qu , good job on giving it a shot. I really like your button idea for the simplicity of NOT having the alert pop up all the time :laughing:. It's a good idea when it comes down to user experience because let's face it, no one's going to land on a search page with alerts flying at you. It's nice that you gave the user a choice.

However, your code is broken as well and it's actually the same thing that's wrong with Sam Muir 's code.

But that's awesome you gave it a shot and did a pen for him to look at. I did the same thing :)

Cheers,

Huck - :sunglasses:

Sam Muir
Sam Muir
16,745 Points

Thanks for the replies team.

Huck - you did the same thing? I can't see a link to a pen - did you forget to add it?

I'm just trying to build on the answer in Dave's video - hence the While Loop used. Buttons would be good for UX, yes, but I'm not trying to improve the UX - I'm just trying to do what was asked.

The break is needed to exit the while loop when the user ops to exit it (using Quit or Cancel), correct? And I've never heard that a loop within a loop being should be avoided before? Pretty much every piece of material that I've read seems to suggest that it's fine?

I think the issue is with the following section of the code (I've removed this and the code works as originally intended in Dave's video).

else { alert('Sorry, no student matches the name ' + search); i = students.length +1; }

After inspecting the code and stepping through it, it looks like the For Loop continues to run through the If & Else loops for every record in the Array. So even if a name does match, it will get overwritten by the code in the Else loop.

I've moved/changed the code "i = student.length +=1;" into the For Loop, so that it exits when the record is matched, and it's now working.

However, this still only returns one Student, so an additional loop needs to be written to return multiple students in the array.

huckleberry
huckleberry
14,636 Points

You replied about 5 seconds before I did :laughing:

Cheers,

Huck - :sunglasses:

huckleberry
huckleberry
14,636 Points

edit: Ok, seriously, what happened to quoting on these forums?

"I'm just trying to build on the answer in Dave's video - hence the While Loop used. Buttons would be good for UX, yes, but I'm not trying to improve the UX - I'm just trying to do what was asked."

Yeah, I know :). Just shooting her a compliment as it was a nice touch. Much better than saying "yours doesn't work either for an array of more than 1 person" lol

"The break is needed to exit the while loop when the user ops to exit it (using Quit or Cancel), correct? And I've never heard that a loop within a loop being should be avoided before? Pretty much every piece of material that I've read seems to suggest that it's fine?"

Yes, you're fine there. There's absolutely nothing wrong with a loop inside of a loop. It's actually quite common and perfectly fine as a standard practice.

"I think the issue is with the following section of the code (I've removed this and the code works as originally intended in Dave's video)."

else { alert('Sorry, no student matches the name ' + search); i = students.length +1; }

YUP! lol

"After inspecting the code and stepping through it, it looks like the For Loop continues to run through the If & Else loops for every record in the Array. So even if a name does match, it will get overwritten by the code in the Else loop."

Not exactly. Your code was always breaking after the first or second iteration at most so unless students[1] had the first name as students[0], it wouldn't have even had the chance to overwrite it.

"I've moved/changed the code "i = student.length +=1;" into the For Loop, so that it exits when the record is matched, and it's now working."

I'll just let you read my post on this one. However, I'm really curious as to why you think you need that there? Please explain your thought process. Would better help me to understand what you're trying to accomplish with that.

"However, this still only returns one Student, so an additional loop needs to be written to return multiple students in the array."

No, an additional loop isn't needed you just need to restructure how you're thinking about approaching it. I know Dave mentioned flags earlier in the course and I already covered it in my code so I'll just shush and let you read and get back to me :laughing:

Good job on narrowing down the problem though. Once you learn how to use Chrome's dev tools it'll become a breeze because you can set 'breakpoints' along the lines of your script and then you make the browser execute your code line by line while you watch it in the console. Becomes muuuuch easier to spot what's wrong when you can literally click through the code executing it one line at a time and then hover over the code to see what the values or conditions are at that point in time in the script.

Cheers,

Huck - :sunglasses:

Sam Muir
Sam Muir
16,745 Points

Whoops - just as I replied I saw you already replied first!

Thanks a lot for the detailed answer - I'll have a read through now and give it a go! :)

Cheers,