Статьи

Сортировка значений с помощью JavaScript

Списки и таблицы часто являются лучшим способом отображения данных в Интернете; но вам не нужно беспокоиться о сортировке этой информации вручную. В сегодняшнем уроке вы создадите плагин jQuery, который с легкостью поместит все ваши утки в ряд!


Итак, как именно работает сортировка в JavaScript? Это не слишком сложно: у любого объекта массива есть метод сортировки. Если вы не передадите ему какие-либо параметры, он преобразует объекты в массиве в строки, сортирует их по алфавиту и возвращает их. Обычно это ужасно; рассмотрите возможность сортировки чисел 0 — 10 по алфавиту. Вы получите это: [0, 1, 10, 2, 3, 4, 5, 6, 7, 8, 9]. К счастью, мы можем передать функцию методу sort. Эта функция должна принимать два параметра (два элемента для сравнения): затем она возвращает 0, если они равны, отрицательное число, если первый параметр имеет приоритет, или положительное число второго параметра должно стоять первым. Так что числа на самом деле проще всего сортировать «вручную»:

1
2
3
numberArray.sort(function(a, b) {
    return a — b
});

Очевидно, это вернет 0, если числа равны, отрицательное число, если a должно быть первым, и положительное число, если b должно быть первым.

Мы рассмотрим сортировку нескольких различных типов данных, несколько в нескольких форматах; но все это будет гораздо полезнее, если мы обернем его в плагин jQuery, поэтому давайте начнем с настройки этой оболочки!

Вы все еще не можете создать плагин jQuery

Если вы не знакомы с написанием плагинов jQuery, ознакомьтесь со скринкастом Джеффри Уэя « Вы все еще не можете создать плагин jQuery? ». Он быстро наберет скорость, если вас устраивает jQuery! (истинное признание: я на самом деле никогда не писал плагин, пока не сделал этот).

Мы настроим наш плагин с именем datasort следующим образом: мы передадим ему массив элементов для сортировки; мы можем указать четыре параметра.

  • тип данных (тип данных, которые вы сортируете)
  • sortElement (дочерний элемент, по которому вы хотите отсортировать, если хотите)
  • sortAttr (атрибут, по которому вы хотите отсортировать, если хотите)
  • обратное (направление, в котором они должны сортироваться)

Таким образом, полностью модифицированный вызов нашего плагина может выглядеть так:

1
2
3
4
5
6
$(‘ul.names li).datasort({
            datatype : ‘alpha’,
            sortElement : ‘span.first’,
            sortAttr : ‘rel’,
            reverse : true
        });

Вот плагин оболочки:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
(function ($) {
  $.fn.datasort = function(options) {
    var defaults = {
        //set the default parameter values
          datatype : ‘alpha’,
          sortElement : false,
          sortAttr : false,
          reverse : false
          },
    // combine the default and user’s parameters, overriding defaults
        settings = $.extend({}, defaults, options),
        datatypes = {},
        base = {},
        that = this;
 
    if (typeof settings.datatype === ‘string’) {
      that.sort(datatypes[settings.datatype]);
    }
    if (typeof settings.datatype === ‘function’) {
      that.sort(settings.datatype);
    }
    if(settings.reverse) {
      that = $($.makeArray(this).reverse());
    }
    $.each(that, function(index, element) { that.parent().append(element); });
  };
})(jQuery);

Вот как это будет работать: мы настроим все переменные в начале. Затем, если параметр типа данных является строкой, мы найдем соответствующую функцию сортировки в объекте типов данных и отсортируем ее; если параметр типа данных является функцией, мы будем сортировать по нему. Наконец, если для параметра reverse установлено значение true, мы изменим порядок отсортированных элементов (поскольку объекты jQuery не являются истинными массивами JavaScript, функция reverse не будет работать с ними; поэтому мы можем использовать $ .makeArray ( ), чтобы превратить его в единое целое, а затем, как только оно перевернется, мы повторно запросим его!).

На самом низком уровне вы можете отсортировать практически любой тип данных одним из двух способов: мы будем называть их по алфавиту и по номерам. Давайте создадим эти две функции как свойства вашего базового объекта.

01
02
03
04
05
06
07
08
09
10
11
12
13
base = {
  alpha : function(a, b) {
    a = a.toUpperCase();
    b = b.toUpperCase();
    return (a < b) ?
    //ternary operator: condition ?
  },
  number : function(a, b) {
    a = parseFloat(a);
    b = parseFloat(b);
    return a — b;
  }
},

Довольно просто, а? Просто нормализуйте два значения, сравните и верните. Сложная часть — это анализ данных, которые мы хотим отправить этим функциям; это то, что мы будем делать сейчас. Однако есть еще одна вещь.

При сортировке элементов в массиве мы можем не захотеть сортировать просто по тексту самого элемента. Для этого используются параметры sortElement и sortAttr нашего плагина. Например, мы, вероятно, захотим отсортировать строки таблицы на основе определенного столбца ячеек таблицы. В этом случае мы будем использовать $ (‘table tr’). Datasort ({sortElement: ‘td.price’}). Или, возможно, мы хотим отсортировать список изображений по их атрибутам alt: $ (‘ul li’). Datasort ({sortElement: ‘img’, sortAttr: ‘alt’}). Из-за всего этого нам нужно добавить еще одну функцию в наш базовый объект:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
base = {
  alpha : function (a, b) { … },
  number : function (a, b) { … },
  extract : function (a, b) {
    var get = function (i) {
      var o = $(i);
      if (settings.sortElement) {
        o = o.children(settings.sortElement);
      }
      if (settings.sortAttr) {
        o = o.attr(settings.sortAttr);
      } else {
        o = o.text();
      }
      return o;
    };
    return {
      a : get(a),
      b : get(b)
    };
  }
},

Это может выглядеть сложно, но это не так. Мы просто создаем объект jQuery с каждым элементом; если sortElement установлен, мы используем метод children (), чтобы получить правильные элементы. Затем, если sortAttr установлен, мы получаем его значение; если нет, мы получаем текст элемента. Мы установили все это для внутренней функции и возвращаем объект с двумя свойствами; эти свойства являются значениями, которые мы должны проанализировать и отправить в соответствующую базовую функцию сортировки.

Это, вероятно, казалось большой подготовительной работой, но мы действительно занимались абстрагированием как можно большего количества кода. Таким образом, они будут намного меньше повторять код, потому что важные действия были объединены в функции.

Наконец-то мы здесь: самое интересное! Мы начнем с создания двух простых функций для нашего объекта типов данных. Они просто передадут значения в base.extract (), а затем передадут эти возвращаемые значения в соответствующий класс сортировки.

01
02
03
04
05
06
07
08
09
10
11
12
13
datatypes = {
  alpha : function (a, b) {
    var o = base.extract(a, b);
    return base.alpha(oa, ob);
  },
  number : function(a, b) {
    var o = base.extract(a, b);
    for (var e in o) {
      o[e] = o[e].replace(/[$]?(-?\d+.?\d+)/, ‘\$1’);
    }
    return base.number(oa, ob);
  },
},

Наш алфавитный сортировщик должен быть очевидным. Сортировщик чисел делает немного больше: перед тем, как передать извлеченные значения, он убирает знак доллара спереди. Я сохранил это регулярное выражение простым, но вы могли бы проанализировать множество различных числовых форматов здесь, если хотите усложнить. Давайте попробуем наш развивающийся плагин; создать простую HTML-страницу:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
<!DOCTYPE html>
<html>
<head>
  <meta charset=’utf-8′ />
  <title>Data Sorting</title>
  <style type=’text/css’>
  ul, table {
    display:table;
    float:left;
    background:#ececec;
    margin:10px;
    padding:0;
    border:1px solid #ccc;
  }
  li, tr {
    margin:0;
    padding:8px;
    border-top:1px solid #fff;
    border-bottom:1px solid #ccc;
    list-style-type:none;
  }
  li:first-child { border-top:0 }
  li:last-child { border-bottom:0 }
  </style>
</head>
<body>
  <table class=’a’>
    <thead>
      <tr>
        <th rel=’alpha’ class=’first’>First Name</th>
        <th rel=’alpha’ class=’last’>Last Name</th>
      </tr>
    </thead>
    <tbody>
      <tr><td class=»first»>Jeffrey</td> <td class=»last»>Way</td></tr>
      <tr><td class=»first»>Sean</td> <td class=»last»>Hodge</td></tr>
      <tr><td class=»first»>Adam</td> <td class=»last»>Miller</td></tr>
      <tr><td class=»first»>Ian</td> <td class=»last»>Yates</td></tr>
      <tr><td class=»first»>Adrian</td> <td class=»last»>Try</td></tr>
      <tr><td class=»first»>Caleb</td> <td class=»last»>Aylsworth</td></tr>
    </tbody>
  </table>
 
  <ul class=’n’>
  <li>4.09</li>
  <li>4.10</li>
  <li>67.8</li>
  <li>100</li>
  <li>-98</li>
  <li>67.7</li>
  <li>23</li>
  </ul>
 
  <ul class=»curr»>
    <li>$299.66</li>
    <li>$299.57</li>
    <li>$0.14</li>
    <li>$80.00</li>
  </ul>
 
  <script src=»http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js» /></script>
  <script src=»jquery.datasort.js» /></script>
  <script type=»text/javascript»>
    $(‘table.a tbody tr’).datasort({sortElement : ‘td.last’});
    $(‘ul.n li’).datasort({datatype: ‘number’, reverse: true});
    $(‘ul.curr li’).datasort({ datatype: ‘number’ });
  </script>
</body>
</html>

Я включил таблицу и два списка (и я их кратко описал). Обратите внимание на наши вызовы плагинов: мы используем тип данных по умолчанию для таблицы, но сортируем по ячейкам таблицы с классом last; попробуйте изменить это на «td.first». Затем мы сортируем списки по номерам и переворачиваем один из них. Вот доказательство наших трудов:

Сортировка по алфавиту и по номерам

Довольно мило, но это были относительно простые значения; Что если мы хотим иметь возможность сортировать несколько форматов для одного типа?

Есть несколько различных способов написания дат, что делает их довольно сложным для анализа их для сортировки. Тем не менее, мы можем покрыть большинство из них этим:

Так что мы здесь делаем? Во-первых, вот логика: если все даты отформатированы в ГГГГММДД, они будут отсортированы правильно с числовой сортировкой. Наш парсер может сортировать следующие форматы даты:

  • YYYY-MM-DD
  • YYYYMMDD
  • DD / MM / YYYY
  • месяц ДД, ГГГГ

Сначала мы удаляем наши дефисы, что оставляет YYYY-MM-DD готовым к анализу. Затем мы заменяем название или аббревиатуру каждого месяца его числовым значением. Наконец, мы должны переставить числа для ДД / ММ / ГГГ и месяца ДД, ГГГГ. Это то, что делают последние два выражения. Чтобы попробовать, вставьте этот список в наш HTML:

01
02
03
04
05
06
07
08
09
10
11
<ul class=’date’>
  <li>2009-10-06</li>
  <li>sept 25, 1995</li>
  <li>1990-06-18</li>
  <li>20100131</li>
  <li>June 18, 2009</li>
  <li>02/11/1993</li>
  <li>15941219</li>
  <li>1965-08-05</li>
  <li>1425-12-25</li>
</ul>

И назовите это так:

1
$(‘ul.date li’).datasort({datatype: ‘date’});
Даты сортировки

Это идеальный парсер дат? Ни в коем случае; мы не можем отсортировать DD / MM / YY, потому что нет никакого способа узнать, в каком веке это происходит. Кроме того, мы не можем определить разницу между DD / MM / YY и MM / DD / YY, поэтому нам просто нужно Выбери один.

Сортировка значений времени должна быть одним из самых сложных значений для сортировки: мы должны иметь возможность принимать 12-часовое, 24-часовое время и значения с или без тегов AM / PM и секунд. Я думаю, что проще всего отсортировать время по алфавиту, хотя все его числа. Почему? Рассмотрим эти две временные метки: 00:15:37 и 12:15. Первый должен идти первым, но если мы отсортируем их по числу, они будут проанализированы как числа с плавающей запятой и в итоге получатся 1537 и 1215. Теперь второе значение будет первым. Кроме того, при сортировке в алфавитном порядке нам не нужно удалять двоеточия (parseFloat () подавит их). Итак, вот как это делается.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
time : function(a, b) {
  var o = base.extract(a, b),
      afternoon = /^(.+) PM$/i;
  for (var e in o) {
    o[e] = o[e].split(‘:’);
    var last = o[e].length — 1;
 
    if(afternoon.test(o[e][last])) {
      o[e][0] = (parseInt(o[e][0]) + 12).toString();
      o[e][last] = o[e][last].replace(afternoon, ‘\$1’);
    }
    if(parseInt(o[e][0]) < 10 && o[e][0].length === 1) {
      o[e][0] = ‘0’ + o[e][0];
    }
    o[e][last] = o[e][last].replace(/^(.+) AM$/i, ‘\$1’);
 
    o[e] = o[e].join(»);
  }
  return base.alpha(oa, ob);
}

Давайте пройдемся по этой строке за строкой.

1
2
var o = base.extract(a, b),
    afternoon = /^(.+) PM$/i;

Начнем с наших переменных: извлеченных значений и регулярного выражения для проверки метки PM.

1
2
3
4
5
6
7
8
for (var e in o) {
  o[e] = o[e].split(‘:’);
  var last = o[e].length — 1;
 
  if(afternoon.test(o[e][last])) {
    o[e][0] = (parseInt(o[e][0]) + 12).toString();
    o[e][last] = o[e][last].replace(afternoon, ‘\$1’);
  }

Далее мы запустим цикл for, просматривая каждое из значений, которые мы сортируем; Сначала мы разбиваем его на массив в двоеточиях. Мы создаем простой способ добраться до последних элементов массива: наша последняя переменная. Затем мы проверяем регулярное выражение PM для последнего элемента в нашем массиве; если он возвращает true, это значение имеет тег PM. Поэтому мы добавим 12 к первому элементу в нашем массиве, который будет значением часа; мы делаем это, потому что нам нужно, чтобы все значения были отформатированы за 24 часа. (Обратите внимание, что для этого мы должны преобразовать его в число, добавить 12, а затем превратить обратно в строку). Наконец, мы снова используем регулярное выражение PM, чтобы удалить эту метку из последнего элемента в массиве.

1
2
3
4
5
6
7
8
    if(parseInt(o[e][0]) < 10 && o[e][0].length === 1) {
      o[e][0] = ‘0’ + o[e][0];
    }
   o[e][last] = o[e][last].replace(/^(.+) AM$/i, ‘\$1’);
 
    o[e] = o[e].join(»);
}
return base.alpha(oa, ob);

В этом последнем фрагменте мы проверяем значение часа для двух условий: оно меньше 10? и строка имеет только один символ? Это важно, потому что значение как 08 будет анализироваться как 8 и будет меньше 10; но мы пытаемся понять, нужно ли нам добавлять ноль на фронт. Если строка имеет только один символ, то мы добавляем ноль, поэтому 3 становится 03. Это будет держать все в порядке!

Перед присоединением к массиву мы удаляем все метки AM. Так что теперь это. , ,

1
2
3
4
5
6
7
8
9
<ul class=’time’>
  <li>1:15:47</li>
  <li>3:45 PM</li>
  <li>12:00:17</li>
  <li>06:56</li>
  <li>19:39</li>
  <li>4:32 AM</li>
  <li>00:15:36</li>
</ul>

, , , можно отсортировать с этим. , ,

И мы сделали! Вот плоды нашего труда:

Время сортировки

Мы настроили наш плагин jQuery, чтобы пользователи могли передавать функции сортировки в качестве параметра типа данных. Это позволяет нам легко расширять плагин, хотя у нас нет доступа к базовому «классу» из вызова плагина. Мы можем легко написать функцию для сортировки psudeo-рейтингов:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
$(‘ul.rating li’).datasort({datatype: function(a, b) {
      var o = {
      a : $(a).text(),
      b : $(b).text()
      }
      for (var e in o) {
        o[e] = o[e].replace(/poor/i, 0)
                   .replace(/satisfactory/i, 1)
                   .replace(/good/i, 2)
                   .replace(/excellent/i, 3);
      }
      return oa — ob;
    }
});

Это использует простейшие регулярные выражения, возможные для сортировки списка следующим образом:

1
2
3
4
5
6
<ul class=»rating»>
  <li>Good</li>
  <li>Excellent</li>
  <li>Poor</li>
  <li>Satisfactory</li>
</ul>

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

(Попробуйте заменить код jQuery для таблицы в первом примере этим!)

Конечно, мы могли бы значительно улучшить этот плагин; Например, мы можем проверить атрибут rel для типа данных, если он не указан в качестве параметра, и по умолчанию использовать альфа, если rel не существует. Но это помимо сортировки.

В целом, чтобы отсортировать с помощью JavaScipt, мы следуем этим шагам:

  1. Определите различные форматы, которые вы хотите отсортировать.
  2. Решите, в каком формате вы хотите отсортировать.
  3. Сортируйте массив элементов с помощью метода sort (), передав функцию, которая преобразует эти два элемента в желаемый формат перед их сравнением.

У вас есть тип данных для добавления в наш плагин? Есть лучший способ сортировки одного из них? Давайте послушаем это в комментариях!

  • Подпишитесь на нас в Твиттере или подпишитесь на ленту Nettuts + RSS для получения лучших учебных материалов по веб-разработке.