Как вы сортируете массив по нескольким столбцам? - 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 ]

144 голосов
/ 07 мая 2010

Если имена владельцев различаются, сортируйте их. В противном случае используйте название публикации для разрешения конфликтов.

function mysortfunction(a, b) {

  var o1 = a[3].toLowerCase();
  var o2 = b[3].toLowerCase();

  var p1 = a[1].toLowerCase();
  var p2 = b[1].toLowerCase();

  if (o1 < o2) return -1;
  if (o1 > o2) return 1;
  if (p1 < p2) return -1;
  if (p1 > p2) return 1;
  return 0;
}
53 голосов
/ 04 августа 2013

Я думаю, что вы ищете, то By.js: https://github.com/Teun/thenBy.js

Позволяет использовать стандартный Array.sort, но со стилем firstBy().thenBy().thenBy().

Пример можно увидеть здесь .

23 голосов
/ 28 марта 2013

Возникла необходимость в смешанных массивах asc и desc объектов в стиле SQL по ключам.

Решение Кеннебека, приведенное выше, помогло мне добраться до этого:

Array.prototype.keySort = function(keys) {

keys = keys || {};

// via
// /29407/dlina-obekta-javascript
var obLen = function(obj) {
    var size = 0, key;
    for (key in obj) {
        if (obj.hasOwnProperty(key))
            size++;
    }
    return size;
};

// avoiding using Object.keys because I guess did it have IE8 issues?
// else var obIx = function(obj, ix){ return Object.keys(obj)[ix]; } or
// whatever
var obIx = function(obj, ix) {
    var size = 0, key;
    for (key in obj) {
        if (obj.hasOwnProperty(key)) {
            if (size == ix)
                return key;
            size++;
        }
    }
    return false;
};

var keySort = function(a, b, d) {
    d = d !== null ? d : 1;
    // a = a.toLowerCase(); // this breaks numbers
    // b = b.toLowerCase();
    if (a == b)
        return 0;
    return a > b ? 1 * d : -1 * d;
};

var KL = obLen(keys);

if (!KL)
    return this.sort(keySort);

for ( var k in keys) {
    // asc unless desc or skip
    keys[k] = 
            keys[k] == 'desc' || keys[k] == -1  ? -1 
          : (keys[k] == 'skip' || keys[k] === 0 ? 0 
          : 1);
}

this.sort(function(a, b) {
    var sorted = 0, ix = 0;

    while (sorted === 0 && ix < KL) {
        var k = obIx(keys, ix);
        if (k) {
            var dir = keys[k];
            sorted = keySort(a[k], b[k], dir);
            ix++;
        }
    }
    return sorted;
});
return this;
};

пример использования:

var obja = [
  {USER:"bob",  SCORE:2000, TIME:32,    AGE:16, COUNTRY:"US"},
  {USER:"jane", SCORE:4000, TIME:35,    AGE:16, COUNTRY:"DE"},
  {USER:"tim",  SCORE:1000, TIME:30,    AGE:17, COUNTRY:"UK"},
  {USER:"mary", SCORE:1500, TIME:31,    AGE:19, COUNTRY:"PL"},
  {USER:"joe",  SCORE:2500, TIME:33,    AGE:18, COUNTRY:"US"},
  {USER:"sally",    SCORE:2000, TIME:30,    AGE:16, COUNTRY:"CA"},
  {USER:"yuri", SCORE:3000, TIME:34,    AGE:19, COUNTRY:"RU"},
  {USER:"anita",    SCORE:2500, TIME:32,    AGE:17, COUNTRY:"LV"},
  {USER:"mark", SCORE:2000, TIME:30,    AGE:18, COUNTRY:"DE"},
  {USER:"amy",  SCORE:1500, TIME:29,    AGE:19, COUNTRY:"UK"}
];

var sorto = {
  SCORE:"desc",TIME:"asc", AGE:"asc"
};

obja.keySort(sorto);

дает следующее:

 0: {     USER: jane;     SCORE: 4000;    TIME: 35;       AGE: 16;    COUNTRY: DE;   }
 1: {     USER: yuri;     SCORE: 3000;    TIME: 34;       AGE: 19;    COUNTRY: RU;   }
 2: {     USER: anita;    SCORE: 2500;    TIME: 32;       AGE: 17;    COUNTRY: LV;   }
 3: {     USER: joe;      SCORE: 2500;    TIME: 33;       AGE: 18;    COUNTRY: US;   }
 4: {     USER: sally;    SCORE: 2000;    TIME: 30;       AGE: 16;    COUNTRY: CA;   }
 5: {     USER: mark;     SCORE: 2000;    TIME: 30;       AGE: 18;    COUNTRY: DE;   }
 6: {     USER: bob;      SCORE: 2000;    TIME: 32;       AGE: 16;    COUNTRY: US;   }
 7: {     USER: amy;      SCORE: 1500;    TIME: 29;       AGE: 19;    COUNTRY: UK;   }
 8: {     USER: mary;     SCORE: 1500;    TIME: 31;       AGE: 19;    COUNTRY: PL;   }
 9: {     USER: tim;      SCORE: 1000;    TIME: 30;       AGE: 17;    COUNTRY: UK;   }
 keySort: {  }

(используя функцию печати из здесь )

вот пример jsbin .

edit: вычищен и выложен как mksort.js на github .

19 голосов
/ 30 сентября 2016

Хороший способ сортировки по многим полям, являющимся строками, - это использовать toLocaleCompare и логический оператор ||.

Что-то вроде:

// Sorting record releases by name and then by title.
releases.sort((oldRelease, newRelease) => {
  const compareName = oldRelease.name.localeCompare(newRelease.name);
  const compareTitle = oldRelease.title.localeCompare(newRelease.title);

  return compareName || compareTitle;
})

Если вы хотите сортировать по большему количеству полей, вы можете просто связать их в цепочке с оператором return с большим количеством логических операторов.

17 голосов
/ 07 мая 2010

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

8 голосов
/ 01 декабря 2015

Я предлагаю использовать встроенный компаратор и связать требуемый порядок сортировки с логическим или ||.

function customSort(a, b) {
    return a[3].localeCompare(b[3]) || a[1].localeCompare(b[1]);
}

Рабочий пример:

var array = [
    [0, 'Aluminium', 0, 'Francis'],
    [1, 'Argon', 1, 'Ada'],
    [2, 'Brom', 2, 'John'],
    [3, 'Cadmium', 3, 'Marie'],
    [4, 'Fluor', 3, 'Marie'],
    [5, 'Gold', 1, 'Ada'],
    [6, 'Kupfer', 4, 'Ines'],
    [7, 'Krypton', 4, 'Joe'],
    [8, 'Sauerstoff', 3, 'Marie'],
    [9, 'Zink', 5, 'Max']
];

array.sort(function (a, b) {
    return a[3].localeCompare(b[3]) || a[1].localeCompare(b[1]);
});

document.write('<pre>');
array.forEach(function (a) {
    document.write(JSON.stringify(a) + '<br>');
});
8 голосов
/ 07 апреля 2014

Вы можете объединить две переменные вместе в ключ сортировки и использовать его для сравнения.

list.sort(function(a,b){
   var aCat = a.var1 + a.var2;
   var bCat = b.var1 + b.var2;
   return (aCat > bCat ? 1 : aCat < bCat ? -1 : 0);
});
4 голосов
/ 18 апреля 2018

Я нашел multisotr . Это простая, мощная и небольшая библиотека для множественной сортировки. Мне нужно было отсортировать массив объектов с динамическими критериями сортировки:

const criteria = ['name', 'speciality']
const data = [
  { name: 'Mike', speciality: 'JS', age: 22 },
  { name: 'Tom', speciality: 'Java', age: 30 },
  { name: 'Mike', speciality: 'PHP', age: 40 },
  { name: 'Abby', speciality: 'Design', age: 20 },
]

const sorted = multisort(data, criteria)

console.log(sorted)
<script src="https://cdn.rawgit.com/peterkhayes/multisort/master/multisort.js"></script>

Эта библиотека более мощная, это был мой случай. Попробуй.

2 голосов
/ 12 января 2016

Я работал с ng-grid и мне нужно было выполнить сортировку по нескольким столбцам в массиве записей, возвращаемых из API, поэтому я придумал эту изящную динамическую функцию мультисортировки.

Прежде всего, ng-grid запускает «событие» для «ngGridSorted» и передает эту структуру обратно, описывая сортировку:

sortData = {
    columns:    DOM Element,
    directions: [], //Array of string values desc or asc. Each index relating to the same index of fields
    fields:     [], //Array of string values
};

Итак, я построил функцию, которая будет динамически генерировать функцию сортировки на основе sortData, как показано выше ( Не пугайтесь полосы прокрутки! Это всего около 50 строк! Кроме того, я извините за помойку. Это помешало горизонтальной полосе прокрутки! ):

function SortingFunction(sortData)
{
    this.sortData = sortData;

    this.sort = function(a, b)
    {
        var retval = 0;

        if(this.sortData.fields.length)
        {
            var i = 0;

            /*
                Determine if there is a column that both entities (a and b)
                have that are not exactly equal. The first one that we find
                will be the column we sort on. If a valid column is not
                located, then we will return 0 (equal).
            */
            while(  (   !a.hasOwnProperty(this.sortData.fields[i]) 
                    ||  !b.hasOwnProperty(this.sortData.fields[i]) 
                    ||  (a.hasOwnProperty(this.sortData.fields[i]) 
                        && b.hasOwnProperty(this.sortData.fields[i]) 
                        && a[this.sortData.fields[i]] === b[this.sortData.fields[i]])
                    ) && i < this.sortData.fields.length){
                i++;
            }

            if(i < this.sortData.fields.length)
            {
                /*
                    A valid column was located for both entities
                    in the SortData. Now perform the sort.
                */
                if(this.sortData.directions 
                && i < this.sortData.directions.length 
                && this.sortData.directions[i] === 'desc')
                {
                    if(a[this.sortData.fields[i]] > b[this.sortData.fields[i]])
                        retval = -1;
                    else if(a[this.sortData.fields[i]] < b[this.sortData.fields[i]])
                        retval = 1;
                }
                else
                {
                    if(a[this.sortData.fields[i]] < b[this.sortData.fields[i]])
                        retval = -1;
                    else if(a[this.sortData.fields[i]] > b[this.sortData.fields[i]])
                        retval = 1;
                }
            }
        }

        return retval;
    }.bind(this);
}

Затем я сортирую результаты моего API (results) следующим образом:

results.sort(new SortingFunction(sortData).sort);

Надеюсь, кому-то еще нравится это решение так же, как и мне! Спасибо!

1 голос
/ 18 декабря 2018

Попробуйте это:

t.sort( (a,b)=> a[3].localeCompare(b[3]) || a[1].localeCompare(b[1]) );

let t = [
    //[publicationID, publication_name, ownderID, owner_name ]
    [1, 'ZBC', 3, 'John Smith'],
    [2, 'FBC', 5, 'Mike Tyson'],
    [3, 'ABC', 7, 'Donald Duck'],
    [4, 'DBC', 1, 'Michael Jackson'],
    [5, 'XYZ', 2, 'Michael Jackson'],
    [6, 'BBC', 4, 'Michael Jackson'],
  ]; 
  
  // owner_name subarrray index = 3
  // publication_name subarrray index = 1

t.sort( (a,b)=> a[3].localeCompare(b[3]) || a[1].localeCompare(b[1]) );

console.log(t.join('\n'));

Я предполагаю, что ваши данные в массиве let t = [ [publicationID, publication_name, ownderID, owner_name ], ... ], где индекс владельца_имя = 3 и имя_публикации = 1.

...