Functonal Programming: Use the reduce Method to Analyze Data

// the global variable
var watchList = [
                 {  
                   "Title": "Inception",
                   "Year": "2010",
                   "Rated": "PG-13",
                   "Released": "16 Jul 2010",
                   "Runtime": "148 min",
                   "Genre": "Action, Adventure, Crime",
                   "Director": "Christopher Nolan",
                   "Writer": "Christopher Nolan",
                   "Actors": "Leonardo DiCaprio, Joseph Gordon-Levitt, Ellen Page, Tom Hardy",
                   "Plot": "A thief, who steals corporate secrets through use of dream-sharing technology, is given the inverse task of planting an idea into the mind of a CEO.",
                   "Language": "English, Japanese, French",
                   "Country": "USA, UK",
                   "Awards": "Won 4 Oscars. Another 143 wins & 198 nominations.",
                   "Poster": "http://ia.media-imdb.com/images/M/MV5BMjAxMzY3NjcxNF5BMl5BanBnXkFtZTcwNTI5OTM0Mw@@._V1_SX300.jpg",
                   "Metascore": "74",
                   "imdbRating": "8.8",
                   "imdbVotes": "1,446,708",
                   "imdbID": "tt1375666",
                   "Type": "movie",
                   "Response": "True"
                },
                {  
                   "Title": "Interstellar",
                   "Year": "2014",
                   "Rated": "PG-13",
                   "Released": "07 Nov 2014",
                   "Runtime": "169 min",
                   "Genre": "Adventure, Drama, Sci-Fi",
                   "Director": "Christopher Nolan",
                   "Writer": "Jonathan Nolan, Christopher Nolan",
                   "Actors": "Ellen Burstyn, Matthew McConaughey, Mackenzie Foy, John Lithgow",
                   "Plot": "A team of explorers travel through a wormhole in space in an attempt to ensure humanity's survival.",
                   "Language": "English",
                   "Country": "USA, UK",
                   "Awards": "Won 1 Oscar. Another 39 wins & 132 nominations.",
                   "Poster": "http://ia.media-imdb.com/images/M/MV5BMjIxNTU4MzY4MF5BMl5BanBnXkFtZTgwMzM4ODI3MjE@._V1_SX300.jpg",
                   "Metascore": "74",
                   "imdbRating": "8.6",
                   "imdbVotes": "910,366",
                   "imdbID": "tt0816692",
                   "Type": "movie",
                   "Response": "True"
                },
                {
                   "Title": "The Dark Knight",
                   "Year": "2008",
                   "Rated": "PG-13",
                   "Released": "18 Jul 2008",
                   "Runtime": "152 min",
                   "Genre": "Action, Adventure, Crime",
                   "Director": "Christopher Nolan",
                   "Writer": "Jonathan Nolan (screenplay), Christopher Nolan (screenplay), Christopher Nolan (story), David S. Goyer (story), Bob Kane (characters)",
                   "Actors": "Christian Bale, Heath Ledger, Aaron Eckhart, Michael Caine",
                   "Plot": "When the menace known as the Joker wreaks havoc and chaos on the people of Gotham, the caped crusader must come to terms with one of the greatest psychological tests of his ability to fight injustice.",
                   "Language": "English, Mandarin",
                   "Country": "USA, UK",
                   "Awards": "Won 2 Oscars. Another 146 wins & 142 nominations.",
                   "Poster": "http://ia.media-imdb.com/images/M/MV5BMTMxNTMwODM0NF5BMl5BanBnXkFtZTcwODAyMTk2Mw@@._V1_SX300.jpg",
                   "Metascore": "82",
                   "imdbRating": "9.0",
                   "imdbVotes": "1,652,832",
                   "imdbID": "tt0468569",
                   "Type": "movie",
                   "Response": "True"
                },
                {  
                   "Title": "Batman Begins",
                   "Year": "2005",
                   "Rated": "PG-13",
                   "Released": "15 Jun 2005",
                   "Runtime": "140 min",
                   "Genre": "Action, Adventure",
                   "Director": "Christopher Nolan",
                   "Writer": "Bob Kane (characters), David S. Goyer (story), Christopher Nolan (screenplay), David S. Goyer (screenplay)",
                   "Actors": "Christian Bale, Michael Caine, Liam Neeson, Katie Holmes",
                   "Plot": "After training with his mentor, Batman begins his fight to free crime-ridden Gotham City from the corruption that Scarecrow and the League of Shadows have cast upon it.",
                   "Language": "English, Urdu, Mandarin",
                   "Country": "USA, UK",
                   "Awards": "Nominated for 1 Oscar. Another 15 wins & 66 nominations.",
                   "Poster": "http://ia.media-imdb.com/images/M/MV5BNTM3OTc0MzM2OV5BMl5BanBnXkFtZTYwNzUwMTI3._V1_SX300.jpg",
                   "Metascore": "70",
                   "imdbRating": "8.3",
                   "imdbVotes": "972,584",
                   "imdbID": "tt0372784",
                   "Type": "movie",
                   "Response": "True"
                },
                {
                   "Title": "Avatar",
                   "Year": "2009",
                   "Rated": "PG-13",
                   "Released": "18 Dec 2009",
                   "Runtime": "162 min",
                   "Genre": "Action, Adventure, Fantasy",
                   "Director": "James Cameron",
                   "Writer": "James Cameron",
                   "Actors": "Sam Worthington, Zoe Saldana, Sigourney Weaver, Stephen Lang",
                   "Plot": "A paraplegic marine dispatched to the moon Pandora on a unique mission becomes torn between following his orders and protecting the world he feels is his home.",
                   "Language": "English, Spanish",
                   "Country": "USA, UK",
                   "Awards": "Won 3 Oscars. Another 80 wins & 121 nominations.",
                   "Poster": "http://ia.media-imdb.com/images/M/MV5BMTYwOTEwNjAzMl5BMl5BanBnXkFtZTcwODc5MTUwMw@@._V1_SX300.jpg",
                   "Metascore": "83",
                   "imdbRating": "7.9",
                   "imdbVotes": "876,575",
                   "imdbID": "tt0499549",
                   "Type": "movie",
                   "Response": "True"
                }
];

// Add your code below this line
let imdb=[];
let arr=watchList.filter((item)=>(item.Director=="Christopher Nolan"));

arr.map((item)=>imdb.push(item.imdbRating));
//arr.filter((item)=>imdb.push(item.imdbRating));

var averageRating=imdb.reduce((total, item)=> total + item);
// Add your code above this line
console.log(arr);

//console.log(arr+"\n"+imdb+"\n"+averageRating);
console.log("\n"+imdb+"\n"+averageRating);

Some questions:

  1. When I run this program in node.js, the array arr is displayed with all the entries for director Christopher Nolan. But the program when run on this fcc site, it just outputs [object Object],[object Object],[object Object],[object Object]. So why is it behaving differently?
  2. The line that I have that uses map and the following commented line that uses filter both work to return an array with only the imdbRating values. So I dont see when to use map and when to use filter.
  3. Why is the reduce function not working? it seems to be written the correct way.

When I use the filter statement I have, I get back an array with all the Nolan directed films. When I use the map statement (or the filter statement that is commented), I get the imdb array that has the different imdbRating values (4 of them). So I get this imdb array with the 4 values whether I use map or filter, I wonder how to know when to use which one. And finally, even though the imdb array has the 4 values, the reduce function gives me the imdb array without the commas, instead of the sum. As I understand, total is the accumulator and item refers to every single numeric data.

As I read it, the problem does say use map and filter to pull the data that we want, namely the imdbRatings values, and then use reduce to give the sum… So what am I doing wrong in the reduce function?

const arr=watchList.map((item)=>{if (item.Director==“Christopher Nolan”) return item.imdbRating;});

gives me 5 items, not 4, the last one is undefined because the director is not christopher nolan, why am I getting 5, the last iteration evaluates to false right?

.map can never change the number of items in the output array. The output array’s size will always be the same as the input. You can however .filter this output to remove the undefineds.

I have the array of imdbRatings in arr = [‘8.8’,‘8.6’,‘9.0’,‘8.3’] and the length of arr is 4
Now to add numbers with the reduce function, where the numbers are in string format, shouldnt the statement be
let tot=arr.reduce((total,n)=>total+parseFloat(n));
I cant get it to work, cause tot prints out as 8.88.69.08.3

If however, I parse the string values when I map it, by saying

const arr2=arr.map((item)=>(parseFloat(item.imdbRating)));
var averageRating=(arr2.reduce((total, item)=> total + item));

I get the array of the ratings as [8.8,8.6,9.0,8.3]
and the correct answer of 8.675.

So, why does the parseFloat work when I map the values from string to floating point, but does not work in the reduce function when I try to convert them?

@Randell,

Yes if the initial value of the accumulator is a string , then any values concatenated to it would first be converted to strings and then appended to the end. But what determines whether the initial value of the accumulator is a string or a number. Im assuming it is the type of the element, but will it be the type of element that is in the original array (type is string) or the type passed into the reduce function (float because the element type passed in is parsed with parseFloat(string).

check the syntax of reduce() in the documentation (linked below), but as explained above if you don’t specify a starting value than the starting value of the accumulator it will be the first value of the array, as your array is of strings, than that is what the type would be

@ilenia,

Yes, one way to specify the type of the accumulator would be to explicitly specify it as an argument, but I was assuming that the type could also be determined according to the second argument that was passed into the reduce function, so it would be a string if the second argument was passed in as a string and a number if the second argument was passed n as a number. I thought by using parseFloat on the string argument before passing it in to the reduce function, we were passing in a number and therefore the type of the accumulator would be number. Guess that’s not the way it works!!

if you don’t specify a second argument to the reduce method, then the callback function will have the element at index 0 as total, and the element at index 1 as item for the first iteration - note that the item at index 0 is taken as it is, no modifications, no change of type or anything, just like that

if you specify a starting value for the accumulator (that you have called total) then total will start with that value and item will be the element at index 0 for the first iteration

so it is useful for you to specify a second argument in the reduce method, it means less work to do in these things

If the first item in the array is a string. But if
@ilenia: Yes, the accumulator initial value should be a string, if the first item gets passed in as a string. But if the first element is passed in as a number then the initial value of the accumulator should be that number. And I thought that by specifying that the first element had to be parsed into a float, we would be passing a number so that the initial value of the accumulator is a number. But I guess it doesn’t work that way, because when I store the array as a collection of numbers (as shown earlier in this post), everything works without a hitch. So, the accumulator type depends on the value of the element in the array, and not the way it is passed into the reduce function.

it depends on the value type of array elements!

if you set a starting value for your accumulator in the reduce() method and use parseFloat() on the item parameter in the callback of reduce() then you don’t need to add the map method and it means less resources needed to run yout code

Try passing an object such as this:

{ TotalRating: 0, MovieTotal: 0 }

in as the starting accumulator; then accumulating and returning it in each iteration through the loop as the new accumulator.

Then add console logging to each iteration to follow the progress of your accumulator.

Finally accept the result and calculate the desired average from the returned TotalRating and MovieCount properties.