Javascript 排列数组元素

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14834571/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 18:05:08  来源:igfitidea点击:

Ranking array elements

javascriptarraysalgorithmsortingranking

提问by Yùz Nagami

I need an algorithm to rank elements of an array in Javascript.

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

Example : I have an array as follows:

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

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

I need to rank the entries by value. So 82 should receive rank 1, 79 rank 2 etc. If two entries have the same value they receive the same rank and the rank for a lower value is raised.

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

So for this array, the new ranking array would be:

所以对于这个数组,新的排名数组将是:

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

How can I do this ?

我怎样才能做到这一点 ?

回答by Denys Séguret

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.map(function(v){ return sorted.indexOf(v)+1 });
console.log(ranks);

Result :

结果 :

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

If you want to be compatible with old browsers, you may have to define a shim for indexOfand for map(note that if you want to do this very fast for very big arrays, you'd better use forloops and use an object as map instead of indexOf).

如果你想与旧浏览器兼容,你可能需要为 indexOfmap定义一个 shim(注意,如果你想对非常大的数组非常快地做到这一点,你最好使用for循环并使用一个对象作为 map而不是indexOf)。

回答by Francis Avila

This won't work with older browsers because it uses ECMAScript 5 features, but it allows you to quickly and succinctly produce an array of rankings even for very large arrays. (It doesn't use indexOfwhich does a linear search and thus can be slow for large arrays.)

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

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

Example output:

示例输出:

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

回答by user5949942

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), []);
}

Usage:

用法:

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] 

Looks a bit ugly, but it doesn't use indexOf()or an object/map, so not only does it run a little faster, but more importantly, it respects the meaning of "same rankedness" as defined by the comparison function. If one uses indexOf()or an object, "same rankedness" can only mean a === bor String(a) === String(b).

看起来有点难看,但是它不使用indexOf()或对象/地图,因此不仅运行速度快一点,而且更重要的是,它尊重比较函数定义的“相同等级”的含义。如果使用indexOf()或 对象,“相同等级”只能表示a === bString(a) === String(b)

Alternatively, use findIndex():

或者,使用findIndex()

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

回答by svinec

I needed the same piece of code for an operations scheduling script I was writing. I used objects and their properties/keys, which can have any value and can be accessed whenever needed. Also, as far as I read in some articles, the search of properties in objects can be faster than search in arrays.

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

The script below has three simple steps:

下面的脚本包含三个简单的步骤:

  1. sort the values (ascending or descending doesn't matter for the rest of the script)

  2. find the ranks and number of occurrences for each value

  3. replace the given values with ranks using the data from step 2

  1. 对值进行排序(升序或降序对于脚本的其余部分无关紧要)

  2. 找到每个值的等级和出现次数

  3. 使用步骤 2 中的数据将给定的值替换为排名

Note! The below script will not output duplicate ranks, but instead increments ranks for duplicate values/elements.

笔记!下面的脚本不会输出重复的等级,而是增加重复值/元素的等级。

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

I also needed to do something else for my script.

我还需要为我的脚本做一些其他的事情。

The above output has the following meaning:

上述输出的含义如下:

  • index - the ID of the element in the input array

  • value - the rank of the element from the input array

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

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

And I needed to basically 'swap the index with the value', so that I have a list of element IDs, arranged in the order of their ranks:

我需要基本上“用值交换索引”,以便我有一个元素 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;}

And some examples:

还有一些例子:

ToBeRanked:

ToBeRanked:

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

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

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

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

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

[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, 7, 7, 7, 7, 7]

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

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

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

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

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

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

rankArrayElements( ToBeRanked ):

rankArrayElements( ToBeRanked ):

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

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

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

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

[0, 2, 8, 1, 5, 7, 4, 3, 6, 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]

[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]

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

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

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

convertRanksToListOfElementIDs( rankArrayElements( ToBeRanked ) ):

convertRanksToListOfElementIDs(rankArrayElements(ToBeRanked)):

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

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

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

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

[0, 3, 1, 7, 6, 4, 8, 5, 2, 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]

[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]

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

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

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

回答by Chris Nadovich

IMHO several solutions here are incorrect as they do not correctly handle values that occur after repeated values. Such followers should get the next rank. The highest rank should equal the number of unique values in the array. This solution (in PHP) is, IMHO, correct. Basically @Suresh's solution with the bugs removed.

恕我直言,这里的几个解决方案是不正确的,因为它们没有正确处理重复值之后出现的值。这样的追随者应该获得下一个排名。最高等级应等于数组中唯一值的数量。这个解决方案(在 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;
   }

回答by Abz

This should work with duplicate keys in the array

这应该适用于数组中的重复键

function rank(arry) {
    let sorted = arry.slice().sort(function (a, b) {
        return b - a
    });


    let currentRank = sorted.length;
    let rankValue = null;
    let ranks = [];

    sorted.forEach(value => {
        if(value !== rankValue && rankValue !==null) {
            currentRank--;
        }

        ranks.push({value,currentRank});
        rankValue = value;
    });

    let mapRanksToArrayValues = arry.map(function (x) {
        let _rank = null;
        ranks.forEach( rank => {
            if(rank.value === x ) {
                _rank =  rank.currentRank;
                return;
            }
        });
        return _rank;
    });

    return mapRanksToArrayValues;
}

回答by Anitta Developer

I created Rank_JS Pro.

我创建了 Rank_JS Pro。

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

Basics Methods:

基本方法:

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

回答by Olian04

This alternate way doesn't require the input array to be sorted:

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

// 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]

回答by Suresh Bala

I am not good at Javascript but in PHP it can be done quite easily the following way. Somebody good at JavaScript can come up with the relevant code.

我不擅长 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;
}