排列数组元素

时间:2013-02-12 14:15:28

标签: javascript arrays algorithm sorting ranking

我需要一种算法来在Javascript中对数组的元素进行排名。

示例:我有一个数组如下:

[79, 5, 18, 5, 32, 1, 16, 1, 82, 13]

我需要按值对条目进行排名。所以82应该获得等级1,79等级2等。 如果两个条目具有相同的值,则它们将获得相同的排名,并且会提高较低值的排名。

因此对于这个数组,新的排名数组将是:

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

我该怎么做?

11 个答案:

答案 0 :(得分:29)

var arr = [79, 5, 18, 5, 32, 1, 16, 1, 82, 13];
var sorted = arr.slice().sort(function(a,b){return b-a})
var ranks = arr.slice().map(function(v){ return sorted.indexOf(v)+1 });

结果:

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

如果您希望与旧浏览器兼容,则可能需要define a shim for indexOfmap(请注意,如果您希望对非常大的阵列执行此操作非常快,则最好使用for循环并使用对象作为地图而不是indexOf)。

答案 1 :(得分:9)

这不适用于旧浏览器,因为它使用ECMAScript 5 features,但它允许您快速简洁地生成排名数组,即使对于非常大的数组也是如此。 (它不会使用进行线性搜索的indexOf,因此对于大型数组来说可能会很慢。)

function cmp_rnum(a,b) {
    // comparison function: reverse numeric order
    return b-a;
}
function index_map(acc, item, index) {
    // reduction function to produce a map of array items to their index
    acc[item] = index;
    return acc;
}
function ranks(v) {
    var rankindex = v.slice().sort(cmp_rnum).reduceLeft(index_map, Object.create(null));
    // reduceLeft() is used so the lowest rank wins if there are duplicates
    // use reduce() if you want the highest rank
    return v.map(function(item){ return rankindex[item]+1; });
}

示例输出:

> ranks([79, 5, 18, 5, 32, 1, 16, 1, 82, 13]);
  [2, 7, 4, 7, 3, 9, 5, 9, 1, 6]

答案 2 :(得分:3)

function rank(arr, f) {
    return arr
    .map((x, i) => [x, i])
    .sort((a, b) => f(a[0], b[0]))
    .reduce((a, x, i, s) => (a[x[1]] =
        i > 0 && f(s[i - 1][0], x[0]) === 0 ? a[s[i - 1][1]] : i + 1, a), []);
}

用法:

rank([79, 5, 18, 5, 32, 1, 16, 1, 82, 13], (a, b) => b - a);
// [2, 7, 4, 7, 3, 9, 5, 9, 1, 6] 

看起来有点难看,但它不使用indexOf()或对象/地图,所以它不仅运行得快一点,更重要的是,它尊重定义的“相同排名”的含义由comparison function。如果使用indexOf()或对象,则“相同排名”只能表示a === bString(a) === String(b)

或者,使用findIndex()

function rank(arr, f) {
    const sorted = arr.slice().sort(f)
    return arr.map(x => sorted.findIndex(s => f(x, s) === 0) + 1)
}

答案 3 :(得分:1)

JavaScript ES6简单的两行解决方案。

var arrayRankTransform = arr => {
  const sorted = [...arr].sort((a, b) => b - a);
  return arr.map((x) => sorted.indexOf(x) + 1);
};

console.log(arrayRankTransform([79, 5, 18, 5, 32, 1, 16, 1, 82, 13]));

答案 4 :(得分:0)

我不擅长Javascript,但在PHP中,可以通过以下方式轻松完成。擅长JavaScript的人可以提出相关代码。

$marks = [79, 5, 18, 5, 32, 1, 16, 1, 82, 13];

public function getRank($marks) {
    $rank = 1; $count = 0; $ranks = [];
    //sort the marks in the descending order
    arsort($marks,1);
    foreach($marks as $mark) {
      //check if this mark is already ranked
      if(array_key_exists($mark, $ranks)) {
       //increase the count to keep how many times each value is repeated
       $count++;
       //no need to give rank - as it is already given
      } else {
        $ranks[$mark] = $i+$j;
        $i++;
      }
    return $ranks;
}

答案 5 :(得分: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 - 输入数组中元素的等级

    我基本上需要'将索引与值交换',以便我有一个元素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]

答案 6 :(得分:0)

恕我直言,这里的几种解决方案是不正确的,因为它们不能正确处理重复值后出现的值。这样的追随者应该获得更高的排名。最高等级应等于数组中唯一值的数量。恕我直言,这种解决方案(在PHP中)是正确的。基本上,@ Suresh的解决方案已删除了错误。

  function rank($marks){
    $rank = 1; $ranks = [];
    rsort($marks,SORT_NUMERIC);
    foreach($marks as $mark) {
      if(!isset($ranks[$mark])) {
        $ranks[$mark] = $rank++;
      }
    }
    return $ranks;
   }

答案 7 :(得分:0)

这应该与数组中的重复键一起工作

,antecedents,consequents,support,confidence,lift
0,(CHOL = 230),(SMOK = y),0.5,1.0,1.2
2,"(AL = 0.0, Age = 45)",(SMOK = y),0.3333333333333333,1.0,1.2
3,"(AL = 0.0, SMOK = y)",(Age = 45),0.3333333333333333,1.0,3.0

答案 8 :(得分:0)

我创建了Rank_JS Pro。

<script>https://cdn.statically.io/gl/maurygta2/mquery/master/Rank Tools/rank.js</script>

基本方法:

var a = {
  b: 2,
  c: 7
}
Rank_Tools.rank(a,(pos,name,value) => {
  return pos + ". "+name+" "+value;
})
// result
// rank1 = 1. c 7
// rank 2 = 2. b 2

答案 9 :(得分:0)

这种替代方法不需要对输入数组进行排序:

// O(n^2)
const rank = (arr) => {
  // Create a temporary array to keep metadata 
  // regarding each entry of the original array
  const tmpArr = arr.map(v => ({
    value: v,
    rank: 1,
  }));

  // Get rid of douplicate values
  const unique = new Set(arr);

  // Loops through the set
  for (let a of unique) {
    for (let b of tmpArr) {
      // increment the order of an element if a larger element is pressent
      if (b.value < a) {
        b.rank += 1;
      }
    }
  }

  // Strip out the unnecessary metadata 
  return tmpArr.map(v => v.rank);
};

console.log(rank([2600, 200, 36, 36, 400, 2, 0, 0]));
// => [1, 3, 4, 4, 2, 5, 6, 6]

console.log(rank([79, 5, 18, 5, 32, 1, 16, 1, 82, 13]));
// => [2, 7, 4, 7, 3, 8, 5, 8, 1, 6]

答案 10 :(得分:0)

我做过同样的作业,而且这个作业很好,如果您是新手,也更容易理解。

function rankings(arr) {
    let rankingsArr = [];
    for (let i = 0; i < arr.length; i++) {
        var rank = 1;
        for (let j = 0; j < arr.length; j++) {
            if (arr[j] > arr[i]) rank++;
        }
        rankingsArr.push(rank);
    }
    return rankingsArr;
}