Ранжирование элементов массива

Мне нужен алгоритм для ранжирования элементов массива в 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] 

Как я могу это сделать?

Ответы

Ответ 1

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]

Если вы хотите быть совместимым со старыми браузерами, вам может потребоваться определить прокладку для indexOf и для map (обратите внимание: если вы хотите сделать это очень быстро для очень больших массивов, лучше использовать циклы for и использовать объект в качестве карты вместо indexOf).

Ответ 2

Это не будет работать со старыми браузерами, поскольку использует функции ECMAScript 5, но позволяет быстро и лаконично создавать массив ранжировок даже для очень больших массивов. (Он не использует 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]

Ответ 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() или объект/карту, поэтому он не только работает немного быстрее, но, что более важно, он учитывает значение "того же ранжирования", которое определяется функция сравнения. Если вы используете indexOf() или объект, то "то же ранжирование" может означать только a === b или String(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)
}

Ответ 4

Я плохо разбираюсь в 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

Мне понадобился тот же кусок кода для планирования операций script, который я писал. Я использовал объекты и их свойства/ключи, которые могут иметь любое значение и могут быть доступны по мере необходимости. Кроме того, насколько я читал в некоторых статьях, поиск свойств в объектах может быть быстрее, чем поиск в массивах.

Ниже приведено script три простых шага:

  • Сортировка значений (по возрастанию или убыванию не имеет значения для остальной части script)

  • найдите ранги и количество вхождений для каждого значения

  • заменить заданные значения на ранжирование, используя данные с шага 2

Внимание! Ниже script не будет выводить повторяющиеся ранги, но вместо этого увеличивает ранги для повторяющихся значений/элементов.

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

Мне также нужно было сделать что-то еще для моего script.

Вышеуказанный вывод имеет следующее значение:

  • index - идентификатор элемента во входном массиве

  • value - ранг элемента из входного массива

И мне нужно было в основном "обменивать индекс со значением", так что у меня есть список идентификаторов элементов, расположенных в порядке их рангов:

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

ИМХО, некоторые решения здесь неверны, так как они неправильно обрабатывают значения, которые появляются после повторных значений. Такие последователи должны получить следующий ранг. Наивысший ранг должен равняться количеству уникальных значений в массиве. Это решение (на 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

Это должно работать с дубликатами ключей в массиве

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

Ответ 8

Я создал 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