How to compute the average rating? : Use the reduce Method to Analyze Data [SOLVED]

The average rating I get is 8.52 whereas FCC’s averageRating should equal 8.675.
If I’m not mistaken, to compute an average of something is to add up all the numbers, then divide by how many numbers there are. In other words it is the sum divided by the count.

// 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 filteredList = watchList.filter((x) => (x.Director = "Cristopher Nolan"))

let mappedList = filteredList.map((x) => ( x.imdbRating));

let numberedList = mappedList.map(Number);

let totalRating = numberedList.reduce((total, rating) => total + rating  )  ;
                        
var averageRating = totalRating / numberedList.length;

// Add your code above this line

console.log(averageRating); 

link of the challenge : https://learn.freecodecamp.org/javascript-algorithms-and-data-structures/functional-programming/use-the-reduce-method-to-analyze-data/

This is a little bit of a sneaky one. The instructions tell you to get the average rating of Nolan’s films. Are all the films in the array Nolan films?

Okay, Avatar is not a Christopher Nolan Film. Thanks

Solved it. Thanks for the heads up. Yeah, its a little sneaky:

let filteredList = watchList.filter((x) => (x.Director === "Christopher Nolan"));

let mappedList = filteredList.map((x) => ( x.imdbRating));

let numberedList = mappedList.map(Number);

let totalRating = numberedList.reduce((total, rating) => total + rating  )  ;
                        
var averageRating = totalRating / numberedList.length;

Sure, that works. I might suggest that you don’t need to loop through the array four times. I think it’s more natural do it all with just two loops:

let filteredList = watchList.filter(f => f.Director === 'Christopher Nolan')
let averageRating = filteredList.reduce((acc, cur) => acc + +cur.imdbRating, 0
)/filteredList.length

Of course, with a little creativity, you could also do it in one pass:

let tot = 0
var averageRating = watchList.reduce((acc, cur) => {
  if (cur.Director === 'Christopher Nolan') {
    tot++
    return acc + +cur.imdbRating
  } else {
    return acc
  }
}, 0)/tot

This last one takes up more lines on the page but would actually be a little faster.

I suppose you could try to put it all in one line:

let tot = 0
let averageRating = watchList.reduce((acc, cur) => cur.Director === 'Christopher Nolan' ? ++tot ? acc + +cur.imdbRating : null : acc, 0)/tot

But at some point it gets ridiculous.

1 Like

@kevinSmith I love the creative solution, but it’s a “functional way” since we use a side effect to get the tot value?

I’m not sure to what side effect you are referring. But it is a functional solution.

I think change the value of tot variable inside the fn is a side effect

A pure functional solution does not depend on the existence of mutable vars. I suggest using filter to just get Nolan’s movies, then map to pull out the ratings. From there you have a list of numbers and its length, so the rest should be obvious.

Or you could gather the total up within your reduce function: just give it a starting state of {count: 0, total: 0} and write the appropriate reducer. Just make sure your reducer returns a new object, don’t just change its props.

Or you could just use a running average formula with reduce and not keep a count at all.

Having variables local to the function that change inside the function is not a side effect. A side effect would be changing something outside the scope of the function. This could be mutating one of the params passed or some other variable from an outside scope. For example, if I’d wrapped that all in a function and passed watchList in as a parameter and then changed watchList that would be a side effect. One quality of a pure function is that it does’t effect anything except for its return value.

Which it appears to be in your example. Anyway, it’s still not purely functional, which is just fine – I use local state all the time. And if there’s no observable state leaking out, then yes, the overall thing is functional.

But the point of the exercise is teaching functional programming using HOFs, and if the functions given to HOFs aren’t pure (forEach notwithstanding) then they aren’t going to be reusable in all contexts. You have to know how to follow the rules before you know when it’s ok to bend them.

The other one is that for any given input, you’ll always get the same output. These two properties are what let you take a pure function and use it anywhere at all without fear it’ll break or break something else. I like to call this combined property “Lack of WTF”. Pure functions are the ultimate Software Legos.

2 Likes

I see what you’re saying. I was thinking of having that all wrapped in a function that itself didn’t have side effects. I originally wrote that almost 2 years ago. I wouldn’t write it that way now.