javascript を使ってテーブルをソートすることになった。ドットインストールを見ると複数項目でソートする方法まで載っている・・・が、これを一般化してどんなテーブルでもソートできるようにするにはどうするか考えてみた。公開されているライブラリもあるんですけどね。
コードは次のとおり。
1 2 3 4 5 6 7 8 9 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 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
var sortFunctions = (function() { var weekday_names = ['日', '月', '火', '水', '木', '金', '土']; /** * 文字列を比較する関数 * 曜日は曜日の順で比較する。 */ var compareString = function(a, b) { a = a.trim(); b = b.trim(); var a_week_index = weekday_names.indexOf(a); var b_week_index = weekday_names.indexOf(b); if (a_week_index >= 0 && b_week_index >= 0) { a = a_week_index; b = b_week_index; } if (a == b) { return 0; } else { if (a > b) { return 1; } else { return -1; } } } /** * 2 つの tr の順序を与えられた order によって比較する * @a * @b * @order_array [[number, order], ...] order は並び順(昇順なら1), number は td の順次(0 から始まる) * @sort_array */ var compareTr = function(a, b, order_array, sort_array) { var a_tds = $(a).find('td'); var b_tds = $(b).find('td'); for (var i = 0; i < sort_array.length; i++) { value = compareString( a_tds.eq(sort_array[i]).text(), b_tds.eq(sort_array[i]).text()); if (value != 0) { return value * order_array[sort_array[i]]; } } return 0; } /** * オーダの配列を生成する。 * @index 変更対象のカラムのindex * @last_index */ var composeOrder = function(index, order_array, sort_array) { // 最後にソート対象になっていた場合は逆順にソートするように設定する。 if (sort_array.length > 0 && index == sort_array[0]) { order_array[index] *= -1; } else { var remove_index = -1; for (var i = 1; i < sort_array.length; i++) { if (sort_array[i] == index) { remove_index = i; break; } } if (remove_index > 0) { sort_array.splice(remove_index, 1); } sort_array.unshift(index); } } /** * 行の並び替え */ var sortRows = function(rows, order_array, sort_array) { rows.sort(function(a, b) { return compareTr(a, b, order_array, sort_array); }); return rows; } var AscChar = '▼'; var DescChar = '▲'; /** * テーブル行の並び替え * @target_table 並び替え対象のテーブル * @orders 並び替えの順番 * @sort_orders 並び替えの優先順位 */ var _sort = function(order_array, sort_array, start_object) { var target_table = $(start_object).parent().parent().parent(); var head = target_table.find('thead>tr>th'); var rows = target_table.find('tbody>tr'); var index = $(head).index(start_object); if (sort_array.length > 0) { var text = $(head).eq(sort_array[0]).text(); if (order_array[sort_array[0]] > 0) { text = text.replace(AscChar, ''); } else { text = text.replace(DescChar, ''); } $(head).eq(sort_array[0]).text(text); } // ソート順序を決定する。 composeOrder(index, order_array, sort_array); // 並び替える。 var new_rows = sortRows(rows, order_array, sort_array); target_table.children('tbody').empty().append(new_rows); var head_text = ""; if (order_array[sort_array[0]] > 0) { head_text += AscChar; } else { head_text += DescChar; } $(head).eq(sort_array[0]).text( head_text + $(head).eq(sort_array[0]).text()); } return {sort: _sort}; })(); |
sortFunction.sort でテーブルをソートして、 ▼(▲) の表示をする。 sort に渡すのは、 order_array, sort_array, クリックされた th 自身 (this) の 3つ。 各パラメータについて下に説明を書いた。
- order_array
-
各列の初期状態の昇順・降順を設定しておく。4列ある場合は [1, 1, 1, 1] のようにする。 1 が昇順、 -1 が降順を表す
。 初期状態でソートされていない場合でも 1 と書いて構わない。ソートをするときに 昇順・降順 が入れ替わる場合は 該当する値を 1 と -1 で入れ替える。 SQL の order by でいうところの ASC, DESC 。
- sort_array
-
初期状態で何番目のカラムでソートされているかを表す配列。 1列目と2列目でソートされており、1列目で最初にソートされている場合は [0, 1] と書く。 特にソートされていない場合は [] と書く。
最後にソートされた列の index は 配列の先頭に追加する。 SQL の order by でいうところの カラム の順序。
- this (th)
どの列がクリックされたかを特定するために、クリックされたものを渡す。
注意点
- 並べ替えられるオブジェクトに対して $(object).click() のようにしてイベントを記述している場合、並べ替え後に同じことをやる必要がある。
- 列の表示名に ▲(▼) を使用すると、ソートの際に消える。
- ▲(▼) は 最後にソートした列のみに表示する。
- 昇順・降順のときの大小の比較は 日, 月, … , 土 が記述されている場合は 日曜を先頭にして考える。文字列は javascript 上 の >, < で比較する。数値の比較には対応していない。
- table の中には 必ず thead, tbody を記述する。 parent() の数が固定で書かれているため。
order の扱いについては、 order by column_name_1 DESC, column_name_2 ASC のように [[0, -1], [1, 1]] で表すことも考えたが、優先順位と降順・昇順を別にしたほうがわかりやすいのではないかと思って上のようになった。
また、ある列で降順でソートされた後に別の列で昇順でソートした場合、最初にソートした降順は保持されるが、今になって降順・昇順は全列で統一したほうがよいのではないかと思っている。