Functional Programming: Use the reduce Method to Analyze Data

Functional Programming: Use the reduce Method to Analyze Data
0.0 0

#1

Hey guys!

I have a question about the FCC problem where you use the reduce method. Link below.
https://learn.freecodecamp.org/javascript-algorithms-and-data-structures/functional-programming/use-the-reduce-method-to-analyze-data/
It has an object containing multiple movies, and the challenge is to find the average score of Christopher Nolan movies.

The code I have is :

var averageRating = watchList
  .filter(movie => {return movie.Director=="Christopher Nolan"})
  .reduce((sum, nolanMovies) => {
    return (sum + parseFloat(nolanMovies.imdbRating));
    },0)
  /(watchList.filter(movie => {return movie.Director=="Christopher Nolan"}).length);  // <--there has to be a better //way.
;

while this works, I feel it is kind of ugly. As you can see, I could filter out the movie list and get the sum of the ratings but didn’t know how to get the amount of movies there are without invoking the filter method again. I feel like there has to be a better way to do this but I just couldn’t find out. What would be the best way so I can convert the sum of scores into an average?


#2

You could increment your count while calculating sum if your accumulator could hold both values. Like an array [0,0] or an object {sum:0, count:0}. That would give you both numbers in one pass, maybe even calculate average at the end somehow.

EDIT
I figured this out. Maybe it will help you out.

const arr = [10,20,30];

const average = arr.reduce(function(acc,el,ind,ar){
  acc = [acc[0]+el, acc[1]+1];
  if(ind >= ar.length-1){  // calculate average on last element
    acc = acc[0] / acc[1];
  }
  return acc;
},[0,0]);

console.log(average);  //20

#3

You can first store the filtered array in a separate variable. That way you only have to do one .filter() call.

var moviesToRate = watchList.filter(...);
var averageRating = moviesToRate.reduce(...) / moviesToRate.length;

Also note that when using arrow functions, if you’re immediately returning a new value, you can omit the braces and the return keyword altogether.

.filter(movie => movie.Director == 'Christopher Nolan')

#4

I like this, definitely more elegant than the way i have it. Thank you!


#5

just completed this challenge, and here is a solution without in-processing variable

var averageRating;

averageRating = watchList
  .filter( el => el.Director == "Christopher Nolan" )
  .map( el => +el.imdbRating)
  .reduce( (acc,cur,index) =>  (acc*index + cur) / (index + 1), 0 )

#6

Could someone help explain in more detail what’s going on with the code below?

  .map( el => +el.imdbRating)
  .reduce( (acc,cur,index) =>  (acc*index + cur) / (index + 1), 0 )

In the map method, I’m curious as to what the + does. For the reduce, I’m just lost. What is acc and cur?


#7

It coerces el.imbRating (a String value) to a number.

First, take a look at the documentation for reduce. If you have any questions about the documentation, let us know.


#8

Appreciate it! Thank you for the help.