Pull to refresh

Быстрая сортировка таблиц посредством Javascript

Reading time 3 min
Views 17K
В процессе работы с таблицами, для удобства восприятия, а также быстрого анализа, рано или поздно возникает вопрос вывода отсортированного содержимого этих таблиц. Эту задачу в web-программировании можно решить двумя способами:

  • Сортировка на стороне сервера посредством SQL или backend'а;
  • Сортировка на стороне клиента.

Минусы первого варианта — это перезагрузка страницы на каждый клик пользователя по контролам сортировки. Плюсы — скорость сортировки на больших выборках.

Минусы сортировки на стороне клиента — сравнительно низкая скорость работы, прямо зависящая от браузера и мощности компьютера пользователя. Плюсы — отсутствие перезагрузки страницы, следовательно, иногда это дает бОльшую скорость получения отсортированного содержимого, т.к. в данном случае нет задержки между отправкой запроса серверу и получением результата.

В данной статье я бы хотел рассмотреть сортировку на стороне клиента средствами JavaScript.


Возьмем таблицу с колонками разного типа данных: числовые данные, время, дата и текст.

Наиболее подходящим методом, я считаю, будет получить содержимое всех ячеек таблицы, по которым нужно вести сортировку и записать его в массив, а затем восстановить строки, согласно их порядку в отсортированном массиве. Для такой сортировки можно будет использовать метод array.sort().

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

Числовые и текстовые данные записываем в массив без изменения.

Время преобразовываем к числовому значению, в моем примере это минуты.
fastSortEngine.prototype.parseTime = function(tvalue) {
  var ret = tvalue;
  if(!tvalue) {
    ret = this.nullValue;
  } else {
    tvalue = tvalue.split(':');
    if(tvalue.length == 2) {
      ret = parseInt(tvalue[0],10)*60 + parseInt(tvalue[1],10);
    } else {
      ret = this.nullValue;
    }
  }
  return ret;
}


Дату тоже преобразовываем к числовому значению. Я использовал для этого объект Date и метод getTime(), который возвращает таймстамп в миллисекундах.
fastSortEngine.prototype.parseDate = function(dvalue) {
  var ret = dvalue;
  if(!dvalue) {
    ret = this.nullValue;
  } else {
    dvalue = dvalue.split(' ');
    if(dvalue.length == 3) {
      var d = new Date(dvalue[2], this.months[dvalue[1].toLowerCase()], dvalue[0]);
      ret = d.getTime();
    } else {
      ret = this.nullValue;
    }

  }
  return ret;
}


Таким образом, числа, дату и время мы сортируем как числовые данные с собственным обработчиком, а текст сортируем обычным вызовом array.sort().

Теперь о том, как эти массивы сопоставить с объектом таблицы. Перед преобразованием мы индексируем все строки таблицы и присваиваем им порядковый номер runiqueID. Это нужно для того, чтобы при сортировке данных с повторяющимися значениями у нас вторым приоритетом был номер строки. Метод array.sort() сортирует массив согласно весам и, передавая ему многомерные массивы, можно их сортировать по нескольким полям. Я использую трехмерный массив, где нулевой элемент — преобразованное значение, первый элемент — порядковый номер строки, а второй — это нода строки. Код обработчика приведен ниже.
fastSortEngine.prototype.sortNumber = function(a,b) {
  var a1 = a[0] == '' ? this.nullValue : parseFloat(a[0]);
  var b1 = b[0] == '' ? this.nullValue : parseFloat(b[0]);
  if   (a1 == b1 && a[1] < b[1] ) return -0.0000000001;
  else if(a1 == b1 && a[1] > b[1] ) return 0.0000000001;
  return a1 - b1;
}


После приведения к нужному типу, сохраняем ноды в элемент массива и вызываем array.sort(), а если нужна сортировка в обратном порядке, то вызываем еще и array.reverse().
var obj = [val,
      rowObj.runiqueID,
      rowObj];
dataCol.push(obj);

if(this.colType == "text") dataCol.sort();
else dataCol.sort(this.sortNumber);


Когда данные отсортированы, мы можем изменять уже и положение строк в дереве таблицы. Проходим по отсортированному массиву и вызываем appendChild для всех, предварительно сохраненных нод строк таблицы.
var l = dataCol.length;
for(var i = 0; i<l; i++) {
  this.tBody.appendChild(dataCol[i][2]);
}


Этот метод очень быстрый, т.к. при сортировке ноды таблицы переставляются только в конце посредством последовательного вызова appendChild.

Посмотреть рабочий пример и скачать исходный код.

UPD: Добавил кеширование результата и слегка оптимизировал код. Также, в примере теперь 500 строк. Оптимизированная версия и исходный код.
Tags:
Hubs:
+33
Comments 84
Comments Comments 84

Articles