Functional Programming: Use the reduce Method ,to Analyze Data

Hi campers, this challenge has taken from me a long time to solve, here it is:
https://learn.freecodecamp.org/javascript-algorithms-and-data-structures/functional-programming/use-the-reduce-method-to-analyze-data/

finally this is my solution :

``````// Add your code below this line
var average=watchList.map(function(item){
return {Director:item.Director,rating:item.imdbRating};
}).filter(function(item){

if(item.Director==="Christopher Nolan"){

return {Director:item.Director,rating:item.imdbRating};
}
}).map(function(value){
let num=Number(value.rating);
return num;
}).reduce(function(sum,currentValue,index,arr){
sum+=currentValue;
if(index===arr.length-1){
return sum/arr.length;
}else {
return sum
}

});

var averageRating=average;

console.log(averageRating);
``````

this worked well

but I donâ€™t know why in reduce method I couldnâ€™t use just if statment without else statment :

``````var average=watchList.map(function(item){
return {Director:item.Director,rating:item.imdbRating};
}).filter(function(item){

if(item.Director==="Christopher Nolan"){

return {Director:item.Director,rating:item.imdbRating};
}
}).map(function(value){
let num=Number(value.rating);
return num;
}).reduce(function(sum,currentValue,index,arr){
sum+=currentValue;
if(index===arr.length-1){
return sum/arr.length;  //here it retuns NAN
}

});
``````

why it retuns NAN ,and my second question did I use a long approach for this challenge .

Reduce must always return a value.

1 Like

Reduce runs the function on the previous value and the current value. Then the return value of that function becomes the previous value, and the function runs on this new previous value and new current value. Then the return value ofâ€¦and so on until you get to the end of your array. If you donâ€™t return anything at any point, the return value will be `undefined`, which will cause the operation to break down, because there will now be no previous value

Also just re the code: I would avoid doing two things in the reduce (ie doing the calculation if youâ€™re on the final item), just make your functions do one specific thing, thereâ€™s no real need to add that complexity - I mean itâ€™s fine, it just makes things slightly confusing. More importantly, thatâ€™s not how `filter` works:

``````if(item.Director==="Christopher Nolan"){
return {Director:item.Director,rating:item.imdbRating};
}
``````

should just be

``````return item.Director==="Christopher Nolan";
``````

At the minute, your code is only working by accident. The function you give to `filter` has to return true or false, it doesnâ€™t return any value other than that. If the function returns true, that item gets kept, if it returns false that item gets dropped. So your code is working because:

• if the director is Christopher Nolan:
``````// this is `true`:
if(item.Director==="Christopher Nolan"){
// so this code runs, and the object coerces to `true`: you are not
// returning an object here, you are just returning the value `true`
return {Director:item.Director,rating:item.imdbRating};
}
``````
• if the director is not Christopher Nolan:
``````// this is `false`:
if(item.Director==="Christopher Nolan"){
// this code does not run
return {Director:item.Director,rating:item.imdbRating};
}
// the function returns `undefined`, which coerces to `false`,
// so the function returns `false`
``````

What `filter` leaves you with is the array you started with, but with any entries where Nolan is not the director dropped from it: you are not returning `{Director: item.Director, rating:item.imdbRating};`

There is also no need to build new objects, it does not make it any easier to access the values - the one you want, `imdbRating` can be accessed like `object.imdbRating` regardless of if there are 2 entries or 2 million entries in the object.

``````var nolanFilms = watchList.filter(function(item){
return item.Director === "Christopher Nolan");
});
var ratings = nolanFilms.map(function(value){
return Number(value.imdbRating);
}).reduce(function(sum,currentValue){
return sum + currentValue;
}, 0);

var averageRating = ratings / nolanFilms.length;
``````

or even:

``````var ratings = watchList.reduce(function(accumulator, film) {
if (film.Director === 'Christopher Nolan') {
// it's a Nolan film: add the rating to the total and increment the number of films
return {
total: accumulator.total + Number(film.imdbRating),
films: accumulator.films + 1;
}
} else {
// not a Nolan film: don't add to the totals
return accumulator;
}
}, {total: 0, films: 0});

return ratings.total / ratings.films;
``````
2 Likes

thank you for your time , you have explained to me this very well .

1 Like

const numOf = watchList.filter(el => el.Director === â€śChristopher Nolanâ€ť).length;
const averageRating = watchList.filter(el => el.Director === â€śChristopher Nolanâ€ť).map(el => Number(el.imdbRating)).reduce((pre, cur) => pre + cur)/ numOf;
console.log(averageRating);