Lab 7: Fetch, Map, Filter
Due: Monday, Mar 31st
In this lab, you will use JavaScript array functions (forEach()
, map()
, and filter()
) to search within an array of movie data entries.
You should consult the following references as needed:
- MDN Javascript reference
- MDN Array reference
- MDN Closures reference
Starter Code¶
Download the following files to get started:
- index.html – An HTML page with Bootstrap.
- funs.js – A JavaScript file with comments.
- data.json – A JSON file containing data from IMDb.
You will modify and submit only the funs.js
file.
You are encouraged to examine the data.json
file to develop an understanding of the structure of the data.
Step 1: Initialization function¶
Your first step is to populate an unordered list based on an array of objects.
The data.json
file contains an array of movie objects based on real data retrieved from IMDb.
Each entry in this array is an object that contains the following information:
Attribute | Description |
---|---|
id |
A unique identifier for the movie |
title |
The name of the movie |
subtitle (optional) |
The movie's subtitle |
series (optional) |
The name of the movie's series |
rating |
The IMDb rating based on user votes (range from 0 to 10) |
runtime |
The runtime of the movie as measured in minutes |
year |
The year the movie was released |
Note that the title
, subtitle
, and series
fields are nested within a name
field.
That is, while you can access movie.id
directly, you would need to access movie.name.title
.
Similarly, the rating
, runtime
, and year
fields are contained within a data
field.
Your first task is to define the setTitleContent()
function that modifies a movie object by copying the movie's title into a new field called titleContent.
(Later functions will modify this new field further.)
Note that this function will be used within a call to map()
, so it will need to return the modified movie object.
Once you've got setTitleContent()
working, you will create a self-executing initialization function where indicated.
Some parts of this code are provided for convenience.
Your main task here is to use fetch()
to retrieve the data from data.json
.
Once you've retrieved the data and parsed it as JSON, you will use map()
(on setTitleContent()
) and forEach()
(using buildItem()
) to build an HTML list dynamically.
Note that buildItem()
relies on the movie's titleContent
field that you add in setTitleContent()
.
Do NOT use any form of loop (such as a for-each loop) for this lab. The purpose is to work with the array functions only.
Finally, for all steps, update the 'target-label'
element to append "N movies" to its textContent
, where N is the number of movies in the list.
The original data set has over 100 movies in it. For this stage, you will just use the first 10 records. Your generated list should look like the one in this screenshot.
In the provided source code, there are hard-coded hostname
and url
variables that your call to fetch()
will use.
Note that your code will not actually access the data.json
file you retrieved.
CORS (a security mechanism we will discuss later) prevents using fetch()
with a local file, so it must retrieve the data from a web server over HTTP.
Step 2: Chaining map calls¶
Your next task is to implement three more functions to append the year, runtime, and rating values to the movie's titleContent
field.
Extend your initialization function so that these functions are chained together using a sequence of map()
calls.
A completed version of this step will produce output such as that shown in this screenshot. Note that the only observable difference here is the additional fields in the list item.
Step 3: Queries and filters¶
Your final task is to implement a group of functions for setting minimum or maximum criteria to filter the data set based on particular values.
You'll start by implementing setMinRating()
and the related functions.
These functions will return a closure that will return a Boolean value (true or false) based on whether a movie would satisfy the condition.
As an example, consider the following code:
let f = setMinRating(8.0);
f
as a function that determines if a movie has at least a minimum rating of 8.0.
For instance, calling f(godfather)
would return true, assuming godfather
refers to the movie object for "The Godfather".
On the other hand, calling f(legend)
would return false for "I Am Legend".
Once you've completed the setMinRating()
function, you cam complete the others that serve similar purposes for year and runtime.
First, note that you need to use parseInt()
and parseFloat()
on the fields that you get from the movie objects.
These fields are originally strings, so you cannot do a basic >
or <
comparisons.
Also make sure that you are returning these comparisons as closures (i.e., return an arrow function) rather than trying to return true or false itself.
Finally, amend your initialization function to filter results.
If the query string is empty (i.e., query
is undefined
), then you should continue to display the first 10 movies.
However, if there is a query, you'll need to get the appropriate minimum or maximum values.
For simplicity, the provided code splits the query string into an array of criteria, such as minrating=8.2
.
The navigation menu at the top of the page contains some embedded query strings for testing purposes. Your results should match those shown in the following screenshots:
- High Rating (rating at least 8.5 – partial list)
- 1970s (year between 1970 and 1979)
- Best Short Movies (less than 100 minutes and rating at least 8.0)
- Blade Runner (just the one movie)
Optional: One more step¶
All of the above examples followed the flow of using a series of calls to filter()
followed by a series of calls to map()
(or vice versa…either works).
But what if you only wanted to make a call to map()
if you were doing a certain filter()
but not otherwise?
That kind of logic doesn't easily lend itself to just chaining functions together.
As a specific example, the last link in the <nav>
bar selects the movies with the highest ratings, but only if they are part of a series.
If the movie fits into this criterion, append the series title to the titleContent
.
The challenge, though, is that you only want to do this if you are selecting this category.
If you are clicking on the "High Rating" link, you don't want to append the text.
As an optional last step, integrate this criterion.
If the query string includes inseries=true
, show only the filtered results that are part of a series (i.e., movie.name.series
is defined).
The complete list should look like this screenshot.
Submission¶
When you are finished, submit funs.js
to Gradescope.
Note this lab will be manually graded.