Как вы сортируете массив по нескольким столбцам? - PullRequest
101 голосов
/ 07 мая 2010

У меня есть многомерный массив. Первичный массив - это массив

[publicationID][publication_name][ownderID][owner_name] 

Я пытаюсь отсортировать массив по owner_name, а затем по publication_name. Я знаю, в JavaScript у вас есть Array.sort(), в который вы можете поместить пользовательскую функцию, в моем случае у меня есть:

function mysortfunction(a, b) {
    var x = a[3].toLowerCase();
    var y = b[3].toLowerCase();

    return ((x < y) ? -1 : ((x > y) ? 1 : 0));
}

Это хорошо для простой сортировки по одному столбцу, а именно, имя_хозяина, но как мне изменить его для сортировки по owner_name, тогда publication_name?

Ответы [ 13 ]

1 голос
/ 06 сентября 2018

У меня была похожая проблема при отображении блоков пула памяти из вывода некоторой композиции виртуальных h-функций DOM. По сути, я столкнулся с той же проблемой, что и сортировка многокритериальных данных, таких как оценка результатов игроков со всего мира.

Я заметил, что многокритериальная сортировка:

- sort by the first column
- if equal, sort by the second
- if equal, sort by the third
-  etc... nesting and nesting if-else

И если вам все равно, вы можете быстро потерпеть неудачу в адском гнусном пламени ... как в аду с обещаниями обратного вызова ...

Что если мы напишем функцию "предиката", чтобы решить, какую часть альтернативы использовать? Предикат просто:

// useful for chaining test
const decide = (test, other) => test === 0 ? other : test

Теперь, после написания ваших классификационных тестов (byCountrySize, byAge, byGameType, byScore, byLevel ...), кому бы вы ни понадобились, вы можете взвесить ваши тесты (1 = asc, -1 = desc, 0 = отключить), поставить их в массиве, и примените сокращающую функцию 'решить' следующим образом:

const multisort = (s1, s2) => {
  const bcs = -1 * byCountrySize(s1, s2) // -1 = desc 
  const ba =  1 *byAge(s1, s2)
  const bgt = 0 * byGameType(s1, s2) // 0 = doesn't matter
  const bs = 1 * byScore(s1, s2)
  const bl = -1 * byLevel(s1, s2) // -1 = desc

  // ... other weights and criterias

  // array order matters !
  return [bcs, ba, bgt, bs, bl].reduce((acc, val) => decide(val, acc), 0)
}

// invoke [].sort with custom sort...
scores.sort(multisort)

И вуаля! Это зависит от вас, чтобы определить свои собственные критерии / веса / заказы ... но вы поняли идею. Надеюсь, это поможет!

EDIT: * убедитесь, что в каждом столбце есть общий порядок сортировки * помните о том, что нет зависимости между порядками столбцов и циклических зависимостей

если нет, сортировка может быть нестабильной!

1 голос
/ 23 января 2015
function multiSort() {

    var args =$.makeArray( arguments ),
        sortOrder=1, prop='', aa='',  b='';

    return function (a, b) {

       for (var i=0; i<args.length; i++){

         if(args[i][0]==='-'){
            prop=args[i].substr(1)
            sortOrder=-1
         }
         else{sortOrder=1; prop=args[i]}

         aa = a[prop].toLowerCase()
         bb = b[prop].toLowerCase()

         if (aa < bb) return -1 * sortOrder;
         if (aa > bb) return 1 * sortOrder;

       }

       return 0
    }

}
empArray.sort(multiSort( 'lastname','firstname')) Reverse with '-lastname'
0 голосов
/ 25 марта 2019

Моя собственная библиотека для работы с итерациями ES6 (blinq) позволяет (помимо прочего) легко выполнять многоуровневую сортировку

const blinq = window.blinq.blinq
// or import { blinq } from 'blinq'
// or const { blinq } = require('blinq')
const dates = [{
    day: 1, month: 10, year: 2000
  },
  {
    day: 1, month: 1, year: 2000
  },
  {
    day: 2, month: 1, year: 2000
  },
  {
    day: 1, month: 1, year: 1999
  },
  {
    day: 1, month: 1, year: 2000
  }
]
const sortedDates = blinq(dates)
  .orderBy(x => x.year)
  .thenBy(x => x.month)
  .thenBy(x => x.day);

console.log(sortedDates.toArray())
// or console.log([...sortedDates])
<script src="https://cdn.jsdelivr.net/npm/blinq@2.0.2"></script>
...