Reputation: 1110
I want to abstract the function that gets passed into my arrays' reduce()
functions to make the function a generic 'greatest of the Array reducer'. To achieve this, I want to then pass in different specific functions in the reduce()
parameters to be able to specify the criteria for comparison. In my example, these are the lines
return players.reduce(topPlayerReducer, players[0], getThreePointerPercentage);
and
return players.reduce(topPlayerReducer, players[0], getReboundNumber);
where topPlayerReducer
is a generic function passed to reduce()
that finds the biggest item in an array based on some criteria. My criteria are the 3rd arguments. How can I incorporate my specific comparison functions (getThreePointerPercentage
and getReboundNumber
) so that I keep this level of abstraction? Right now, I'm getting the error that fn
is not a function in topPlayerReducer
. I'm not surprised by this, because my other functions aren't within the scope. I've also tried doing
var reboundReducer = topPlayerReducer.bind(getReboundNumber);
return gameInfo.players.reduce(reboundReducer, players[0]);}
but I've gotten the same error.
I realize I can achieve a result with making two different functions for reduce()
, but that doesn't satisfy me. I want to know if there is a way to do it differently.
function getGuardWithMostThreePointers(gameInfo){
return players.reduce(topPlayerReducer, players[0], getThreePointerPercentage);
}
function getPlayerWithMostRebounds(gameInfo){
return players.reduce(topPlayerReducer, players[0], getReboundNumber);
}
function topPlayerReducer(topPlayer, currentPlayer, fn){
if (fn(currentPlayer) > fn(topPlayer)){
return currentPlayer;
} else {
return topRebounder;
}
}
function getReboundNumber(player){
return parseInt(player.rebounds_offensive) + parseInt(player.rebounds_defensive);
}
function getThreePointerPercentage(player){
return parseInt(player.three_pointers_made) / parseInt(player.three_pointers_attempted);
}
Upvotes: 3
Views: 90
Reputation: 3857
I would do it like so:
Change the implementation of topPlayerReducer
so that it returns a function which compares two players, rather than comparing the players itself:
function topPlayerReducer(fn){
return function(topPlayer, currentPlayer) {
if (fn(currentPlayer) > fn(topPlayer)){
return currentPlayer;
} else {
return topPlayer;
}
}
}
Then you can call reduce
like so:
return pointGuards.reduce(topPlayerReducer(getThreePointerPercentage), pointGuards[0]);
or
return gameInfo.players.reduce(topPlayerReducer(getReboundNumber), gameInfo.players[0]);
This way you can pass in a custom function with each different call you make to reduce
, you just 'wrap it up' in topPlayerReducer
first. I think this is what you were trying to achieve with bind
.
FYI: I think what you were looking for from bind
is something called partial application, where you take a function with multiple arguments, supply some but not all of the arguments, and get back a function which expects the remaining arguments.
You can do this with bind
, but you have to remember:
this
within the function, So your attempt to use bind would have worked if you'd made these changes:
// Make fn the leftmost parameter
function topPlayerReducer(fn, topPlayer, currentPlayer){
if (fn(currentPlayer) > fn(topPlayer)){
return currentPlayer;
} else {
return topRebounder;
}
}
// Add an extra 'null' argument to be bound to the `this` variable
return players.reduce(topPlayerReducer.bind(null, getReboundNumber), players[0])
For my money, the bind
version just adds clutter in this situation. bind
can be useful when you have functions which make use of this
, though, and you need a way to change its value.
Upvotes: 5