Use .reduce to analyze data - how am I doing so far?

Can someone advise on my progress for this please?

The challenge says to use .map but I don’t see why it’s needed here atm.

I am under the impression that my use of .filter allows the object with director == James Cameron to be eliminated so we are left with all objects with director == Christropher Nolan as requested by challenge.

Is my use of filter fine?

The .reduce part is the tricky part, i tried using element.imdbRating to access each object’s imdbRating value. This results in NaN however…

I then asked if it was because the imdbRating values are strings so I used parseFloat(element.imdbRating) but this still led to the NaN result

Finding an average involves dividing by how many there are which I used filteredByNolan.length to represent - is this correct?


// 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"
                }
];

var averageRating;

var filteredByNolan = watchList.filter ( (x) => x.Director === "Christopher Nolan");

console.log(filteredByNolan);


averageRating = filteredByNolan.reduce ( (total, element) => (total + element.imdbRating)/filteredByNolan.index);

console.log(averageRating);

Your filtering part is good, but you need to modify your reduce function. You were on the right track with parseFloat, the problem is that you’re trying to divide within the reduce function. You should do the divide part after the reduce function has totalled up all the scores as a number. You will also need to add an intial value of 0 to your reduce function. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce for more.

Thank you. This is my revision but alas, still returns NaN

averageRating = filteredByNolan.reduce( function (total, element){ 

    total+parseFloat(element.imdbRating);

  return total / filteredByNolan.index;

},0);

Trying to debugg myself, I added
console.log(parseFloat(element.imdbRating));

inside the function after total+…

now, it seems every correct imdbRating value does print fine:
8.8
8.6
9
8.3

however, when doing console.log( total + parseFloat(element.imdbRating) ) it gives NaN

so I currently think it has something to do with my use of total.

I thought the accumulator is just a var that collects the data, so in this case, it should add all of parseFloat(element.imdbRating)

You should not do the divide part inside the reduce function. Do it after. Also, you should not divide by filteredByNolan.index , as filteredByNolan is an array and will not have the .index property.

Wait, what? I got your first point but .index cannot be used?

If that is the case, then why does a for loop version of the solution i.e.

function calcAvg (obj) {
let total = 0;
  for ( let i = 0 ; i<obj.length ; i++) {

total = total + parseFloat(obj[i].imdbRating);


  }

  return total/obj.length;

}

so calcAvg(filteredByNolan) will have filteredByNolan.index be used in the code and it returns the average as required.

Very confused now…

Do a console.log of filteredByNolan.index and you’ll see why you can’t use it.

Okay got it, so how can you measure the length in general then for this circumstances?

var initialValue = 0;
var sum = [{x: 1}, {x:2}, {x:3}].reduce(
    (accumulator, currentValue) => accumulator + currentValue.x
    ,initialValue
);

console.log(sum)

I am trying to emulate the above, so is

total+parseFloat(element.imdbRating);

fine?

Yes, returning total+parseFloat(element.imdbRating) in your reduce function is fine. The length is taken of the array you feed into reduce, so in your case this value is filteredByNolan.length.

But you just said before that I shouldn’t use filteredByNolan.length :thinking:

I said you shouldn’t use filteredByNolan.index.

facepalm

Absolute misread failure from my end then, I meant to use filteredByNolan.length all along but for some weird reason used .index before and thought that was the same as .length , same as in I thought I wrote .length but clearly didn’t!

My bad, and thanks

No problem. A lot of debugging is like this.

1 Like

Got it done :slight_smile:

var filteredByNolan = watchList.filter ((prop) => prop.Director === 'Christopher Nolan');
console.log(filteredByNolan);

var findTotal = filteredByNolan.reduce( (total, element) =>

total + parseFloat(element.imdbRating),0)


console.log(findTotal);

averageRating = findTotal / filteredByNolan.length;

console.log(averageRating);

Heeding your advice in regards to not implementing the divide inside the reduce function, I chose to simply store the results of the .reduce in a var called findTotal then subsequently make a new var averageRating which divides findTotal by the length of filteredByNolan

I’m not sure if this is ‘‘efficient’’ per say but you obviously know more than me, what is your opinion?

It’s fine defining extra variables like you have as it makes your code more readable to others. You should put [spoiler] tags on lines before and after your solution so that people who don’t want to see a working solution don’t accidentally see it. The second tag should begin with ‘[/’.

1 Like