具有相等排名的JavaScript数组排序/排名

时间:2014-06-15 08:34:59

标签: javascript

根据对象的属性值(" rating"在这种情况下)对对象数组进行排序后,如果每个对象的某些值之间存在联系,如何关联每个对象的排名?这是一个例子:

//Should be tied for 1st Rank
var obj1 = { 
  name: "Person1",
  rating: 99
}

//Should be 3rd Rank
var obj2 = {
  name: "Person2",
  rating: 50
}

//Should be 2nd Rank
var obj3 = {
  name: "Person3",
  rating: 98
}

//Should be 4th Rank
var obj4 = {
  name: "Person4",
  rating: 0
}

//Should be tied for 1st Rank
var obj5 = {
  name: "Person5",
  rating: 99
}

据我所知:

var clients = [obj1, obj2, obj3, obj4, obj5];
var sorted = [];

for (var i = 0; i < clients.length; i++) {
  sorted.push(clients[i]);
}

sorted.sort(function(a, b) {
  return b.rating-a.rating;
});

最终,我希望能够使用对象名称获得排名,如下所示:

alert(sorted.indexOf(obj5) + 1);

7 个答案:

答案 0 :(得分:1)

第二次尝试:虽然不完全存在 - 我认为将排名分为不同的属性而不是依靠indexOf来找到排名是要走的路。然后,当有平局时,你可以更清楚地操纵。仍在努力。将关注最佳解决方案

for(var i = 0; i < sorted.length; i++) {
    // original ranking
     sorted[i].rank = i + 1; 

}

function sortRanking() {
  for(i=0; i< sorted.length; i++) {
    var current = sorted[i];
    var next = sorted[i + 1];

    if(next === undefined || next.rating !== current.rating) {
      console.log("we are done");
      return "done";
    } 


    if(next.rating === current.rating) {

      for(var j = next + 1; j < sorted.length; j++) {
            sorted[j].rank = sorted[j-1].rank;
    }

    next.rank = current.rank;

} 

}
}
sortRanking();
console.log(sorted);

第一次尝试 - 玩了一会儿。这是从原始逻辑中添加的解决方案:

var clients = [o1, o2, o3, o4];
var sorted = [];

for (var i = 0; i < clients.length; i++)
sorted.push(clients[i]);

sorted.sort(function (a, b) {

return clients.rating - clients.rating;
});



function checkForTieAndRating(x) {

// x parameter for object of interest
// need to get the one in front to determine if it is tied
// get index of obj of interest
var indexOfInterest = clients.indexOf(x);
var indexOfBefore = indexOfCurrent -1;

// if obj of interest is ranked #1 then return
if(indexOfBefore < 0) {
return indexOfInterest + 1;
} else {
// get the actual object before this one so you can check rating. put in variable so you can compare.
var objBefore = clients[indexOfBefore];
var ratingOfObjBefore = objBefore.rating;
if(ratingOfObjBefore === x.rating)
  return "Tied for" + indexOfInterest;
 }

}


// check ranking and if tie
checkForTieAndRating(obj2);

// other issue going this route - would be to then 1) alter the objects ranking following the objs that are tied - to 

//Possible alternative solution: After working and about to submit it - I think it would be better to add a ranking property after the sort and manipulate the rankings from there if there are any tied.

答案 1 :(得分:0)

如果你想在同一个地方有几个记录,你应该使用一个额外的立即数组,有效地对元素进行分组。

我会用lodash方便,你应该明白这一点。

_.chain(clients).groupBy('rating').pairs().sortBy(0).reverse().pluck(1).value();

此时您无法使用indexOf,因此您需要编写自己的getRank。

再次,在lodash的帮助下

// returns zero when no rank is found
var getRank = function(sortedArray, object) {
    return 1 + _.findIndex(sortedArray, function(list) {
        return _.contains(list, object);
    });
};

完整的工作小提琴:http://jsfiddle.net/4WJN3/1/

答案 2 :(得分:0)

创建了一个有效的解决方案,尽管很难看。感谢jamie用于此的一些框架:

for (var i = 0; i < clients.length; i++) {
  sorted.push(clients[i]);
}

sorted.sort(function(a, b) {
  return b.rating-a.rating;
});

for(var i = 0; i < sorted.length; i++) {
    // original ranking
     sorted[i].rank = i + 1; 
}


function sortRanking() {
  for (var k = 0; k < sorted.length; k++) {
    for (var h = 1; h < sorted.length + 1; h++) {
      if (sorted[k+h] !== undefined) {
        if (sorted[k+h].tie !== true) {
          if (sorted[k].rating === sorted[h + k].rating) {
            sorted[k].rank = k + 1;
            sorted[h + k].rank = k + 1;
            sorted[k].tie = true;
            sorted[h + k].tie = true;
          }
        }
      }    
    }
  }
}

sortRanking();
alert("Rank: " + obj3.rank);

答案 3 :(得分:0)

我需要为我正在编写的操作调度脚本提供类似的代码。我使用了对象及其属性/键,它们可以具有任何值,并且可以在需要时访问。另外,就我在一些文章中所读到的,对象中属性的搜索可能比在数组中搜索更快。

以下脚本有三个简单的步骤:

  1. 对值进行排序(升序或降序对于脚本的其余部分并不重要)

  2. 查找每个值的排名和出现次数

  3. 使用第2步

  4. 中的数据将等级替换为等级

    请注意!下面的脚本不会输出重复的排名,而是为重复的值/元素增加排名。

    function rankArrayElements( toBeRanked ) {
    
    // STEP 1
    var toBeRankedSorted = toBeRanked.slice().sort( function( a,b ) { return b-a; } ); // sort descending
    //var toBeRankedSorted = toBeRanked.slice().sort( function( a,b ) { return a-b; } ); // sort ascending
    
    var ranks = {}; // each value from the input array will become a key here and have a rank assigned
    var ranksCount = {}; // each value from the input array will become a key here and will count number of same elements
    
    // STEP 2
    for (var i = 0; i < toBeRankedSorted.length; i++) { // here we populate ranks and ranksCount
        var currentValue = toBeRankedSorted[ i ].toString();
    
        if ( toBeRankedSorted[ i ] != toBeRankedSorted[ i-1 ] ) ranks[ currentValue ] = i; // if the current value is the same as the previous one, then do not overwrite the rank that was originally assigned (in this way each unique value will have the lowest rank)
        if ( ranksCount[ currentValue ] == undefined ) ranksCount[ currentValue ] = 1; // if this is the first time we iterate this value, then set count to 1
        else ranksCount[ currentValue ]++; // else increment by one
    }
    
    var ranked = [];
    
    // STEP 3
    for (var i = toBeRanked.length - 1; i >= 0; i--) { // we need to iterate backwards because ranksCount starts with maximum values and decreases
        var currentValue = toBeRanked[i].toString();
    
        ranksCount[ currentValue ]--;
        if ( ranksCount[ currentValue ] < 0 ) { // a check just in case but in theory it should never fail
            console.error( "Negative rank count has been found which means something went wrong :(" );
            return false;
        }
        ranked[ i ] = ranks[ currentValue ]; // start with the lowest rank for that value...
        ranked[ i ] += ranksCount[ currentValue ]; // ...and then add the remaining number of duplicate values
    }
    
    return ranked;}
    

    我还需要为我的剧本做点别的事。

    以上输出具有以下含义:

    • index - 输入数组中元素的ID

    • value - 输入数组中元素的等级

    我需要基本上用值&#39;交换索引,以便我有一个元素ID列表,按照他们的等级排列:

    function convertRanksToListOfElementIDs( ranked ) {  // elements with lower ranks will be first in the list
    
    var list = [];
    
    for (var rank = 0; rank < ranked.length; rank++) { // for each rank...
        var rankFound = false;
        for (var elementID = 0; elementID < ranked.length; elementID++) { // ...iterate the array...
            if ( ranked[ elementID ] == rank ) { // ...and find the rank
                if ( rankFound ) console.error( "Duplicate ranks found, rank = " + rank + ", elementID = " + elementID );
                list[ rank ] = elementID;
                rankFound = true;
            }
        }
        if ( !rankFound ) console.error( "No rank found in ranked, rank = " + rank );
    }
    
    return list;}
    

    还有一些例子:

    ToBeRanked:

    [36,33,6,26,6,9,27,26,19,9]

    [12,12,19,22,13,13,7,6,13,5]

    [30,23,10,26,18,17,20,23,18,10]

    [7,7,7,7,7,7,7,7,7,7]

    [7,7,7,7,7,2,2,2,2,2]

    [2,2,2,2,2,7,7,7,7,7]

    [0,1,2,3,4,5,6,7,8,9]

    rankArrayElements(ToBeRanked):

    [0,1,8,3,9,6,2,4,5,7]

    [5,6,1,0,2,3,7,8,4,9]

    [0,2,8,1,5,7,4,3,6,9]

    [0,1,2,3,4,5,6,7,8,9]

    [0,1,2,3,4,5,6,7,8,9]

    [5,6,7,8,9,0,1,2,3,4]

    [9,8,7,6,5,4,3,2,1,0]

    convertRanksToListOfElementIDs(rankArrayElements(ToBeRanked)):

    [0,1,6,3,7,8,5,9,2,4]

    [3,2,4,5,8,0,1,6,7,9]

    [0,3,1,7,6,4,8,5,2,9]

    [0,1,2,3,4,5,6,7,8,9]

    [0,1,2,3,4,5,6,7,8,9]

    [5,6,7,8,9,0,1,2,3,4]

    [9,8,7,6,5,4,3,2,1,0]

答案 4 :(得分:0)

function rank(arr) {
    var ret = [];
    var s = [];
    var i = 0;
    var _key_;
    for (_key_ in arr) {
        var v;
        v = arr[_key_];
        if (!s[v]) {
            s[v] = ++i;
        }
        ret.push( {
            'Mark': v,
            'Rank': s[v]
        });
    }
    return ret;
}
var marks = [ 
    65,
    41,
    38,
    38,
    37,
    37,
    92,
    84,
    84,
    84,
    83
];
marks.sort(function(a, b) {
  return b-a;
});
var rank = rank(marks);
 console.log(rank);

答案 5 :(得分:0)

使用 ES6,您可以按照以下方法进行操作,即向每个客户端添加属性 rank。试试下面的代码片段。

function setRanks(clients) {
    let currentCount = -1, currentRank = 0,
        stack = 1; // consecutive clients with same rating
    for (let i = 0; i < clients.length; i++) {
        const result = clients[i];
        if (currentCount !== result['rating']) {
            currentRank += stack;
            stack = 1;
        } else {
            stack++;
        }
        result['rank'] = currentRank;
        currentCount = result['rating'];
    }
}

// get the rank using the object name
function getRank(clientName) {
  return clients.find(c => c.name === clientName)['rank'];
}

//Should be tied for 1st Rank
var obj1 = { 
  name: "Person1",
  rating: 99
}

//Should be 3rd Rank
var obj2 = {
  name: "Person2",
  rating: 50
}

//Should be 2nd Rank
var obj3 = {
  name: "Person3",
  rating: 98
}

//Should be 4th Rank
var obj4 = {
  name: "Person4",
  rating: 0
}

//Should be tied for 1st Rank
var obj5 = {
  name: "Person5",
  rating: 99
}

var clients = [obj1, obj2, obj3, obj4, obj5];

clients.sort((c, other) => other.rating - c.rating);
setRanks(clients);
console.log(clients);

console.log(getRank('Person5'));

答案 6 :(得分:-1)

简洁,高效,灵活。
具有相同分数的项目具有相同的等级,但是下一个不同的分数将获得一个n(基于索引)的等级变化。输入必须为array sorted by values of the sourceColumn。两个版本的代码,选择一个您喜欢的版本:

  • for(){ }循环
  • array.map()

var studentsSortedByGrades = [ 
  { name: "A", grade: 5 },
  { name: "B", grade: 3 },
  { name: "C", grade: 3 },
  { name: "D", grade: 2 },
];

var addRankFORLOOP = function(sortedArr,sourceColumn,newColumn){
  for(var i = 0; i<sortedArr.length; i++){ //
    sortedArr[i][newColumn] =
      i===0 || sortedArr[i][sourceColumn] !== sortedArr[i-1][sourceColumn]  ? i+1 // anytime new grade appears, rank=i
        : sortedArr[i-1][newColumn] // elseIf: equal grade, then equal rank
  }
  return sortedArr;
};

/*//OR
var addRankMAP = function(sortedArr,sourceColumn,newColumn){
  return sortedArr.map((item,i) => {
    item[newColumn] = i===0 || sortedArr[i][sourceColumn] !== sortedArr[i-1][sourceColumn]  ? i+1 // anytime new grade appears, rank=i
        : sortedArr[i-1][newColumn] // elseIf: equal grade, then equal rank
    return item; })
}; /**/
var withRanks = addRankFORLOOP(studentsSortedByGrades,'grade','rank');
console.log(withRanks) // ranks: 1,2,2,4