简体   繁体   中英

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

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:

  1. that bind takes an extra argument which is bound to this within the function,
  2. that the arguments you are 'pre-loading' will fill in from the left .

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM