В процессе работы с таблицами, для удобства восприятия, а также быстрого анализа, рано или поздно возникает вопрос вывода отсортированного содержимого этих таблиц. Эту задачу в web-программировании можно решить двумя способами:
Минусы первого варианта — это перезагрузка страницы на каждый клик пользователя по контролам сортировки. Плюсы — скорость сортировки на больших выборках.
Минусы сортировки на стороне клиента — сравнительно низкая скорость работы, прямо зависящая от браузера и мощности компьютера пользователя. Плюсы — отсутствие перезагрузки страницы, следовательно, иногда это дает бОльшую скорость получения отсортированного содержимого, т.к. в данном случае нет задержки между отправкой запроса серверу и получением результата.
Возьмем таблицу с колонками разного типа данных: числовые данные, время, дата и текст.
Наиболее подходящим методом, я считаю, будет получить содержимое всех ячеек таблицы, по которым нужно вести сортировку и записать его в массив, а затем восстановить строки, согласно их порядку в отсортированном массиве. Для такой сортировки можно будет использовать метод array.sort().
По-умолчанию массивы сортируются по-возрастанию и содержимое рассматривается как текст, но можно использовать свой обработчик для метода array.sort. Поэтому, перед тем как использовать этот метод нужно исходные данные подготовить.
Числовые и текстовые данные записываем в массив без изменения.
Время преобразовываем к числовому значению, в моем примере это минуты.
Дату тоже преобразовываем к числовому значению. Я использовал для этого объект Date и метод getTime(), который возвращает таймстамп в миллисекундах.
Таким образом, числа, дату и время мы сортируем как числовые данные с собственным обработчиком, а текст сортируем обычным вызовом array.sort().
Теперь о том, как эти массивы сопоставить с объектом таблицы. Перед преобразованием мы индексируем все строки таблицы и присваиваем им порядковый номер runiqueID. Это нужно для того, чтобы при сортировке данных с повторяющимися значениями у нас вторым приоритетом был номер строки. Метод array.sort() сортирует массив согласно весам и, передавая ему многомерные массивы, можно их сортировать по нескольким полям. Я использую трехмерный массив, где нулевой элемент — преобразованное значение, первый элемент — порядковый номер строки, а второй — это нода строки. Код обработчика приведен ниже.
После приведения к нужному типу, сохраняем ноды в элемент массива и вызываем array.sort(), а если нужна сортировка в обратном порядке, то вызываем еще и array.reverse().
Когда данные отсортированы, мы можем изменять уже и положение строк в дереве таблицы. Проходим по отсортированному массиву и вызываем appendChild для всех, предварительно сохраненных нод строк таблицы.
Этот метод очень быстрый, т.к. при сортировке ноды таблицы переставляются только в конце посредством последовательного вызова appendChild.
Посмотреть рабочий пример и скачать исходный код.
UPD: Добавил кеширование результата и слегка оптимизировал код. Также, в примере теперь 500 строк. Оптимизированная версия и исходный код.
- Сортировка на стороне сервера посредством 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 строк. Оптимизированная версия и исходный код.