Welcome to the Treehouse Community

The Treehouse Community is a meeting place for developers, designers, and programmers of all backgrounds and skill levels to get support. Collaborate here on code errors or bugs that you need feedback on, or asking for an extra set of eyes on your latest project. Join thousands of Treehouse students and alumni in the community today. (Note: Only Treehouse students can comment or ask questions, but non-students are welcome to browse our conversations.)

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and a supportive community. Start your free trial today.

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

Tassia Paschoal
Tassia Paschoal
6,535 Points

Having trouble with the optional challenge at the end of the video. Please help!

The challenge is to be able to print all records with the same value for the "name" property from the student array of objects onto the page. The second part of the challenge to give the user an alert if their input does not match with a student name in the object array.

My issue is that I am having trouble displaying results for records that do match in the array. No matter what name I type, I always get my alert box, when instead it should be displaying all the results for, say, "Jody." Any advice would be helpful!

var students = [ 
  { 
   name: 'Jody',
    track: 'Front End Development',
    achievements: 158,
    points: 14730
  },
  {
    name: 'Jody',
    track: 'iOS Development with Swift',
    achievements: '175',
    points: '16375'
  },
  {
    name: 'Jordan',
    track: 'PHP Development',
    achievements: '55',
    points: '2025'
  },
  {
    name: 'John',
    track: 'Learn WordPress',
    achievements: '40',
    points: '1950'
  },
  {
    name: 'Trish',
    track: 'Rails Development',
    achievements: '5',
    points: '350'
  }
];
var message = '';
var student;
var answer;

//print record in the #output div
function print(message) {
  var outputDiv = document.getElementById('output');
  outputDiv.innerHTML = message;
}

function getStudentReport(student) {
  var report = '';
  report += '<h2>Student: ' + students[prop].name + '</h2>';
  report += '<p>Track: ' + students[prop].track + '</p>';
  report += '<p>Points: ' + students[prop].points + '</p>';
  report += '<p>Achievements: ' + students[prop].achievements + '</p>';
  return report;
}


//loop through selecting student record and hydrating the template
while (true) {
  answer = prompt("Search student records: type a name and press 'OK',   or type 'quit' to end.");

  if (answer === null || answer.toLowerCase() === 'quit') {
    break;
  }
  for (var prop in students) { 
    student = students[prop];
    if (student.name.toLowerCase() === answer.toLowerCase()) {
      message = getStudentReport(student);
      print(message);
    }    
    else {
      //Add alert box
      alert (answer + " is not in our records");
      break;
      }
    }
  } 
nico dev
nico dev
20,364 Points

Hi Tassia Paschoal , I found your code really nice and learned from it in the process :) Thank you for sharing it.

Also, I learned this interesting filter() method that's so extremely useful from Rogier, so thanks to him also!

However, I also was playing around with your code, trying to understand what you tried to there and do it as similar as I could, modifying it the minimum possible.

What I found is that with the for... in loop, the first thing the code will do is:

Comparing the first name of the students with the answer typed, so if, for instance, you typed 'trish' (which is a correct name and should be in the list), the loop will go first to the if and check the condition 'is "dave" (first name) equal to "trish"'?

Since the condition is false, it will go straight to the else statement, and will give you the alert 'trish is not in our records.'

Even if you type 'dave' or 'Dave', since with the update in JS, the things that should be printed to the HTML will only be printed after execution finished (I don't know if that is the correct term to call it), such 'dave' and all the props in the object will only be printed after the loop checked and compared dave with the second name, jody, and again, found the condition for the if (jody === dave) false, went to the else statement, prompted the alert, and with the following break statement, finished the loop and started the while again.

The solution I found was bringing the alert into another if clause, outside the for loop, but of course still within the while.

Additionally, since you want more than one student printed if they have similar name (and maybe more useful if you want more than one student's data, too), I also brought the print function outside the for loop, and instead of assigning the value of getStudentReport, I added to it the previous value of the variable message each time there's a match. That way every time there is one student (or many) with the name the person is searching, it will be added to the list to be printed at the end of the search, when the person either close (null) or type 'quit'.

Like this:

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

  if (answer === null || answer.toLowerCase() === 'quit') {
    break;
  }
  for (var prop in students) {
    student = students[prop];


    if ( student.name.toLowerCase() === answer.toLowerCase() ) {
      message += getStudentReport(student);

    }

  }
  print(message);
  if ( message === '' ) {
    alert(answer + " is not in our records")
  };
}

Hope this helps. Let me know your feedback! What do you think? I am also learning so I can learn from it.

7 Answers

Rogier Nitschelm
seal-mask
.a{fill-rule:evenodd;}techdegree
Rogier Nitschelm
iOS Development Techdegree Student 5,461 Points

I think you are on your way already. I think you will be able to figure it out, but if not, perhaps consider this:

Instead of the for-in loop, maybe you could use the javascript array-method called .filter(). It returns an array with all the matching elements.

  var foundStudents = students.filter(function(stud){
    return stud.name.toLowerCase() === answer.toLowerCase();
  })

  if (foundStudents.length > 0) {
    foundStudents.forEach(function(stud){
      alert(getStudentReport(stud))
    })
  } else {
    alert('Could not be found');
  }

The getStudentReport could then also be changed:

function getStudentReport(stud) {
  var report = '';
  report += '<h2>Student: ' + stud.name + '</h2>';
  report += '<p>Track: ' + stud.track + '</p>';
  report += '<p>Points: ' + stud.points + '</p>';
  report += '<p>Achievements: ' + stud.achievements + '</p>';
  return report;
}
Dmitri Antonov
Dmitri Antonov
Courses Plus Student 6,010 Points

Can you explain more detailed how filter() method works basis above example?

nico dev
nico dev
20,364 Points

Hi Dmitri Antonov,

If this helps, like you, I also didn't know about the filter method, but when I read Rogier above, I searched for it in the MDN here, and you should check that article. It's pretty clear, giving examples and all. And it's nice that is also translated to many languages.

Basically, what says there is that the filter() method makes a test, and afterwards creates an array only with those elements that pass such test, or that fulfill the condition you're looking for. You can then use that array, just like Rogier did above. Clever one, indeed!

In the case above the condition passed as a function within the filter() method was if the student name (fully in lowercase) is equal to the answer provided (also lowercase, just in case they type weird or starting caps or anything, right?). If that happens to be true, that value will be included in the variable foundStudents (array).

Hope that makes things more clear to you, but otherwise feel free to get back. Also, you must check the article in MDN linked above. It is very clear indeed.

Dmitri Antonov
Dmitri Antonov
Courses Plus Student 6,010 Points

Hi Nico, i'm still confused. As far as i understand, first we need to create a function which holds if conditional statement and returns if condition is true.

For example: function getStudentByName(stud) { if (stud.name.toLowerCase() === answer.toLowerCase() { return stud; } }

then we need to assign a new array with filter function:

var foundStudents = students.filter(getStudentbyName(stud))

then:

if (foundStudents.length > 0) { foundStudents.forEach(getStudentByName(stud)) { alert (getStudentReport(stud)); } } else { alert ("could not be found"); }

Am i understanding it correctly? What does forEach() method stand for here?

nico dev
nico dev
20,364 Points

Hi again Dmitri Antonov,

Sorry for my delayed reply. During the weekend I was nowhere to be found :)

I guess I see your points.

Thus, first, I recommend you (as a fellow student) that when you're puzzled like this (like I am all the time :)) you try to open a Workspaces (or your own editor) create different files and experiment with the solutions proposed and the things you don't seem convinced by, etc. Like that, every single time, you learn a new thing from the other fellow students, too.

If you do so in this case, you'll notice that:

1) I cannot answer in behalf of Rogier, but I can perceive and would be almost sure, his answer was not so much a final js file, as a head start for Tassia to continue figuring out but with some guide. You will notice, for instance, that in this case, it works as a one-answer-per-request thing (in other words, you have to reload if you want to inquire about more than one student). The alert will show the html tags, etc. In other words, it works as a guide for the student who asked to find the path back.

2) Your own code seems to be lacking a couple of closing brackets. (By the way, it makes it so much easier if you post the code with the appropriate formatting. That is, in short, in a new line, press three times the key under ESC on your keyboard (backtick), then enter, then start typing/pasting code, then once you finish enter, and finally again the three backticks. For seeing example and so on, please read the "Markdown Cheatsheet" that appears at the bottom of the box where you type an answer to this. That will help you and others a lot! :)

Having said that, about the forEach, it's difficult to explain it for another newbie student like me, but you surely can learn tones of this article of MDN (reading documentation is a really healthy habit for us). Staying just with the videos is like eating fast food all the time. :)

Last but not least: I guess that one of your doubts is related to the order, and that's a valid one, but you definitely should check this article that explains about hoisting in JavaScript, or in other words, that variables and functions are available even before they are defined, as long as they are ever defined. If you're looking at me like I'm an alien, that was also my first reaction :) but take a read to the article and will do what I can't, will make it clearer.

Hope that helped. :)

Yugo Breivo
PLUS
Yugo Breivo
Courses Plus Student 2,583 Points

If you try to do it with methods, that contain only in this course, check my solution.

var message = '';
var student;
var search;
var outOfReport = 0;

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

while (true) {
  search = prompt('Type the name of student or type "quit" to close search');
  if ( search === null || search.toLowerCase() === 'quit' ) {
    break;
  } 
  for (var i = 0; i < students.length; i += 1) {
    student = students[i].name;
    if ( search === student ) {
      message += '<h2>Name: ' + students[i].name + '</h2>';
      message += '<p>Track: ' + students[i].track + '</p>';
      message += '<p>Achivements: ' + students[i].achievements + '</p>';
      message += '<p>Points: ' + students[i].points + '</p>';
    } 
// if no matches, we give +1 to our counter
// if counter = the number of objects in the array, it means no matches at all
    else {
      outOfReport += 1;
    }
  }
  // alert appears, if no matches found, and give 0 to counter for another loop
  if ( outOfReport === students.length ) {
    alert(search + ' is not our student!');
    outOfReport = 0;
  }
}

print(message);
Rogier Nitschelm
seal-mask
.a{fill-rule:evenodd;}techdegree
Rogier Nitschelm
iOS Development Techdegree Student 5,461 Points

Filter is an array-method that loops over every item in an array and returns a new array. The new array contains only items that meet the condition that we test. We test this by returning a conditional for every item in the array.

[ 1, 2, 3, 4, 5, 6, 7].filter(number => number > 1) .

Here I tell the filter-method to filter all numbers that are bigger than 1.

// returns [2, 3, 4, 5, 6, 7]

Other example:

const people = [
 { name: 'Hank', age: 10 },
 { name: 'Marie', age: 44 },
 { name: 'Marie', age: 29 },
 { name: 'Jack', age: 30 }
];

const peopleNamedMarie = people.filter(person => person.name === 'Marie')
// returns [{ name: 'Marie', age: 44 }, { name: 'Marie', age: 29 }];

const adults = people.filter(person => person.age >= 18)
// returns an array of people with an age of 18 and over

I am using ES6 arrow-functions in my example with an implicit return. You can also write:

array.filter(item => {
  return item === someComparison // Note that we must return 
})

array.filter(function(item) { 
  return item === someComparison // Note that we must return 
})

So in short: you can call .filter on any array, and then test each item in the loop to see if it meets a condition. If it does, then it is added into the new array. If not, then it is skipped. It is always a good idea to experiment with things like this. There are good sites for this, one that you can use is https://babeljs.io/repl/ jsbin, etc.

Dmitri Antonov
Dmitri Antonov
Courses Plus Student 6,010 Points

Hi Rogier, thx a lot for above...Found it very useful.

Below two examples of codes. One with filter() and forEach() methods through anonymous function. And another with ordinary function ( same purpose ). First code is working, in second one i'm receiving parameter stud is not defined. Can you guess why?)))...In both codes i'm using stud as parameter, but the second code snippet is not working..

var match = "David"; var students = [

{ name: "David", age: 23, course: "Java", level: "Noobie" },

{ name: "David", age: 24, course: "CS", level: "Professional" },

{ name: "Brigita", age: 28, course: "PHP", level: "intermediate" },

{ name: "John", age: 23, course: "HTML", level: "Rookie" }

];

function print(report) { var output = document.getElementById("output"); output.innerHTML = report; }

function getStudentReport(stud) { var report = '<h2>Student: ' + stud.name + '</h2>'; report += '<p>Track: ' + stud.age + '</p>'; report += '<p>Points: ' + stud.course + '</p>'; report += '<p>Achievements: ' + stud.level + '</p>'; return report; }

var filteredStudents = students.filter(function(stud) { if (stud.name.toLowerCase() === match.toLowerCase()) { return stud; } })

filteredStudents.forEach(function(stud) { document.write(getStudentReport(stud)); })

======================================================================================

var match = "David"; var students = [

{ name: "David", age: 23, course: "Java", level: "Noobie" },

{ name: "David", age: 24, course: "CS", level: "Professional" },

{ name: "Brigita", age: 28, course: "PHP", level: "intermediate" },

{ name: "John", age: 23, course: "HTML", level: "Rookie" }

];

function print(report) { var output = document.getElementById("output"); output.innerHTML = report; }

function getStudentReport(stud) { var report = '<h2>Student: ' + stud.name + '</h2>'; report += '<p>Track: ' + stud.age + '</p>'; report += '<p>Points: ' + stud.course + '</p>'; report += '<p>Achievements: ' + stud.level + '</p>'; return report; }

function studentsFilter (stud) { if (stud.name.toLowerCase() === match.toLowerCase()) { return stud; } }

var filteredStudents = students.filter(studentsFilter(stud));

function result (stud) { print(getStudentReport(stud)); }

var result = filteredStudents.forEach(result(stud)); document.write(result);

============================================================================================

one more thing which i have noticed just now. In case i use below code in the end, only one object is being printed as output however using console.log(filteredStudents) it can be seen that array is ok and contains 2 objects with name David.

var filteredStudents = students.filter(stud => stud.name.toLowerCase() === match.toLowerCase());

filteredStudents.forEach(function(stud) { print(getStudentReport(stud)); })

Rogier Nitschelm
seal-mask
.a{fill-rule:evenodd;}techdegree
Rogier Nitschelm
iOS Development Techdegree Student 5,461 Points

Hello Dmitri,

When you use .filter and you pass in a named function, you do not have to call the function inside the parentheses.

You could just do:

var array = [1, 2, 3]
function someFunction(arg) {
 console.log(arg)
}
array .filter(someFunction)   
// notice that you do not have to do something like array.filter(someFunction((someArg))
// just array.filter(someFunction) will do.

Regarding the last part. You should be able to do this:

filteredStudents.forEach(getStudentReport)

Remember that filter returns an array and forEach does not. The forEach method only iterates over all the items in an array and executes whatever you tell it to once for every item, but it does not return an array. That means that the result of this:

var someResult = filteredStudents.forEach(result);

Will be undefined.

Because you are using a forEach, it might very well be that it looks like it only prints one item, but it might actually print one item at a time for each match (in this case 2 times.).

I am not able to debug at this moment to see what the problem is exactly. However, if you wish to loop/iterate over an array and also return an array, you should try .map instead.

[1, 2, 3, 4, 5].map(number => number)

// returns [1, 2, 3, 4, 5]
  • only works if you have es6 available
Dmitri Antonov
Dmitri Antonov
Courses Plus Student 6,010 Points

Hi Rogier! Thank you very much for above. It's really what i was looking for!

I've changed getStudentReport function a bit:

function getStudentReport(stud) { var report = '<h2>Student: ' + stud.name + '</h2>'; report += '<p>Track: ' + stud.age + '</p>'; report += '<p>Points: ' + stud.course + '</p>'; report += '<p>Achievements: ' + stud.level + '</p>'; document.write(report); }

function studentsFilter (stud) { if (stud.name.toLowerCase() === match.toLowerCase()) { return stud; } }

var filteredStudents = students.filter(studentsFilter);

filteredStudents.forEach(getStudentReport);

And in that case forEach() method works fine and i receive two objects with names Dave as output.

===============================================================================

But when using below code, i receive only one object as output. That seems strange :)))

function getStudentReport(stud) { var report = '<h2>Student: ' + stud.name + '</h2>'; report += '<p>Track: ' + stud.age + '</p>'; report += '<p>Points: ' + stud.course + '</p>'; report += '<p>Achievements: ' + stud.level + '</p>'; return report; }

function studentsFilter (stud) { if (stud.name.toLowerCase() === match.toLowerCase()) { return stud; } }

var filteredStudents = students.filter(studentsFilter);

function printStudent (stud) { print(getStudentReport(stud)); }

filteredStudents.forEach(printStudent);

Rogier Nitschelm
seal-mask
.a{fill-rule:evenodd;}techdegree
Rogier Nitschelm
iOS Development Techdegree Student 5,461 Points

The reason that it don't work is probably because at every iteration (every student it loops over), it exits/pauses the loop to print that student. As every iteration is just 1 student, it can at anytime only print the student that is being iterated over at that very moment. Whereas document.write doesn't exit, but adds each piece of html until its finished. The result being the sum of all html you wrote.

So in short:

  • document.write -> sums up all blocks of html (1 + 1 + 1 = 3)
  • print -> print one block of html at a time (1) (1) (1)

If you want to print both objects,

var match = "David"; var students = [
{ name: "David", age: 23, course: "Java", level: "Noobie" },
{ name: "David", age: 24, course: "CS", level: "Professional" },
{ name: "Brigita", age: 28, course: "PHP", level: "intermediate" },
{ name: "John", age: 23, course: "HTML", level: "Rookie" }
];

function getStudentReport(stud) { var report = '<h2>Student: ' + stud.name + '</h2>'; report += '<p>Track: ' + stud.age + '</p>'; report += '<p>Points: ' + stud.course + '</p>'; report += '<p>Achievements: ' + stud.level + '</p>'; document.write(report); }

function studentsFilter (stud) { if (stud.name.toLowerCase() === match.toLowerCase()) { return stud; } }
var filteredStudents = students.filter(studentsFilter);

print(filteredStudents.forEach(getStudentReport))

The reason this should work is that we call the print-method only once, rather than on every iteration. As print itself is already a built-in function, you do not need a printStudent-function.

Dmitri Antonov
Dmitri Antonov
Courses Plus Student 6,010 Points

Thank You!

This is the output i received:

Students

undefined

Student: David

Track: 23

Points: Java

Achievements: Noobie

Student: David

Track: 24

Points: CS

Achievements: Professional:

Where does undefined come from?

Rogier Nitschelm
seal-mask
.a{fill-rule:evenodd;}techdegree
Rogier Nitschelm
iOS Development Techdegree Student 5,461 Points

Could you paste the code that produced this out put (whole code)?

I pasted the code I provided into https://babeljs.io/ and it prints:

Student: David Track: 23 Points: Java Achievements: Noobie Student: David Track: 24 Points: CS Achievements: Professional

Student: David Track: 24 Points: CS Achievements: Professional

Dmitri Antonov
Dmitri Antonov
Courses Plus Student 6,010 Points

below code. I also pasted to babeljs.io and output was fine without undefined))))

var match = "David"; var students = [

{ name: "David", age: 23, course: "Java", level: "Noobie" },

{ name: "David", age: 24, course: "CS", level: "Professional" },

{ name: "Brigita", age: 28, course: "PHP", level: "intermediate" },

{ name: "John", age: 23, course: "HTML", level: "Rookie" }

];

function print(report) { var output = document.getElementById("output"); output.innerHTML = report; }

function getStudentReport(stud) { var report = '<h2>Student: ' + stud.name + '</h2>'; report += '<p>Track: ' + stud.age + '</p>'; report += '<p>Points: ' + stud.course + '</p>'; report += '<p>Achievements: ' + stud.level + '</p>'; document.write(report); }

function studentsFilter (stud) { if (stud.name.toLowerCase() === match.toLowerCase()) { return stud; } }

var filteredStudents = students.filter(studentsFilter);

print(filteredStudents.forEach(getStudentReport));

Rogier Nitschelm
seal-mask
.a{fill-rule:evenodd;}techdegree
Rogier Nitschelm
iOS Development Techdegree Student 5,461 Points

If the output is fine there then I am afraid I cannot help you further (as there could be an x number of reasons for this to happen, and we could at most guess why). I would take the result you get @ babeljs.io as the correct output, and continue your coding quest :)