如何用Underscore.js按赛车手过往名次及积分排序积分榜对象?
解决赛车联赛积分榜的多优先级排序问题
你的问题在于每次循环调用_.sortBy都会完全重新排序,覆盖之前的结果,而不是在已有排序的基础上进行二次排序。_.sortBy是按照你提供的单个键来排序的,不会保留之前的排序层级。我们需要用多条件优先级排序来实现你的需求,下面是两种可行的方案:
方案1:使用原生Array.sort自定义比较函数
原生的排序方法支持自定义比较逻辑,能完美处理多优先级的排序规则,代码更直观:
var standings = [{ driver: "Bob", points: 45, positions: { 0: 1, 1: 0, 2: 1, 3: 2, 4: 0 } }, { driver: "Mandy", points: 17, positions: { 0: 0, 1: 2, 2: 4, 3: 0, 4: 1 } }, { driver: "Robert", points: 24, positions: { 0: 0, 1: 1, 2: 5, 3: 3, 4: 0 } }]; // 自动获取名次数量,不用硬编码,适配不同规模的联赛 var driverCount = Object.keys(standings[0].positions).length; standings.sort(function(a, b) { // 第一优先级:积分降序,分数高的排前面 if (a.points !== b.points) { return b.points - a.points; } // 第二优先级:从第1名到最后一名依次比较完赛次数,次数多的排前面 for (var i = 0; i < driverCount; i++) { var aCount = a.positions[i] || 0; var bCount = b.positions[i] || 0; if (aCount !== bCount) { return bCount - aCount; } } // 所有条件都相同,保持原顺序(返回0即可) return 0; }); console.log(standings);
方案2:使用Underscore.js的_.sortBy返回排序键数组
Underscore的_.sortBy支持返回一个数组作为排序键,它会按数组元素的顺序依次比较(先比第一个元素,再比第二个,以此类推)。我们可以把积分(取负数实现降序)和各个名次的完赛次数组合成一个数组:
var standings = [{ driver: "Bob", points: 45, positions: { 0: 1, 1: 0, 2: 1, 3: 2, 4: 0 } }, { driver: "Mandy", points: 17, positions: { 0: 0, 1: 2, 2: 4, 3: 0, 4: 1 } }, { driver: "Robert", points: 24, positions: { 0: 0, 1: 1, 2: 5, 3: 3, 4: 0 } }]; var driverCount = Object.keys(standings[0].positions).length; // 生成排序键数组:[-积分, positions[0], positions[1], ...] standings = _.sortBy(standings, function(driver) { var sortKeys = [-driver.points]; // 用负积分实现降序排序 for (var i = 0; i < driverCount; i++) { sortKeys.push(driver.positions[i] || 0); } return sortKeys; }); // 注意:_.sortBy是升序排序,因为我们用了负积分,所以结果已经是正确的顺序,不需要额外reverse console.log(standings);
为什么你的原代码不行?
你之前的循环每次调用_.sortBy(standings, 'positions['+i+']').reverse(),会把数组完全按照当前positions[i]排序,之前的排序结果会被覆盖。比如第一次按positions[0]排序,第二次按positions[1]排序时,会忽略之前的positions[0]的排序结果,导致优先级逻辑完全失效。
两种方案都能完美实现你的需求:先按积分降序,积分相同则从第1名到最后一名依次比较完赛次数,次数多的车手排名更靠前。
内容的提问来源于stack exchange,提问作者Shane Smith
相关产品推荐
相关产品推荐

