db2791 db2791 - 2 months ago 15
Javascript Question

Higher-order reduce() function

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);
}

Answer

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.