Добавление дополнительной сортировки в скрипт на JavaScript

Я не знаю языка JS. Но в моём проекте на Django понадобился скрипт для сортировки таблиц на странице html.

Я воспользовался находящимся в общем доступе скриптом: github. И мне понадобилось сделать так, чтобы при сортировке по определённому столбцу, содержащему много одинаковых значений, дополнительно упорядочивались значения в соседнем столбце.

Я расписал алгоритм словами и попытался повторить его на JS в коде скрипта. Суть проста: из уже отсортированного множества строк через set берутся уникальные значения в искомом сортируемом столбце. Для каждого значения в set находятся все строки с ним. Каждый набор строк для определённого значения в set во временном множестве сортируется и добавляется в новое общее множество. Множество первично отсортированных строк приравнивается ко множеству с вторичной сортировкой.

Но код не работает. При попытке сортировки в странице все строки пропадают. Значит, во множество вторично отсортированных строк ничего не пишется, так понимаю.

Моё добавление к оригинальному коду содержит комментарии на русском, начиная со строки "var new_rows = [] // Объявляем пустое множество 1", и заканчивая "rows = new_rows". Остальной код, который не мой, полностью рабочий, достаточно сделать комментарием строку "rows = new_rows".

Подскажите, пожалуйста, в чём ошибка?

document.addEventListener('click', function (e) {
  var down_class = ' dir-d '
  var up_class = ' dir-u '
  var regex_dir = / dir-(u|d) /
  var regex_table = /\bsortable\b/
  var element = e.target

  function reClassify(element, dir) {
    element.className = element.className.replace(regex_dir, '') + dir
  }

  function getValue(element) {
    // If you aren't using data-sort and want to make it just the tiniest bit smaller/faster
    // comment this line and uncomment the next one
    // return element.getAttribute('data-sort') || element.innerText
    return element.innerText
  }

  if (element.nodeName === 'TH') {
    try {
      var tr = element.parentNode
      // var table = element.offsetParent; // Fails with positioned table elements
      // this is the only way to make really, really sure. A few more bytes though... ??
      var table = tr.parentNode.parentNode
      if (regex_table.test(table.className)) {
        var column_index
        var nodes = tr.cells

        // reset thead cells and get column index
        for (var i = 0; i < nodes.length; i++) {
          if (nodes[i] === element) {
            column_index = i
          } else {
            reClassify(nodes[i], '')
          }
        }

        var dir = down_class

        // check if we're sorting up or down, and update the css accordingly
        if (element.className.indexOf(down_class) !== -1) {
          dir = up_class
        }

        reClassify(element, dir)

        // extract all table rows, so the sorting can start.
        var org_tbody = table.tBodies[0]

        // get the array rows in an array, so we can sort them...
        var rows = [].slice.call(org_tbody.rows, 0)

        var reverse = dir === up_class

        // sort them using custom built in array sort.
        rows.sort(function (a, b) {
          var x = getValue((reverse ? a : b).cells[column_index])
          var y = getValue((reverse ? b : a).cells[column_index])
          // var y = (reverse ? b : a).cells[column_index].innerText
          // var x = (reverse ? a : b).cells[column_index].innerText
          return isNaN(x - y) ? x.localeCompare(y) : x - y
        })

        var new_rows = [] // Объявляем пустое множество 1
        var unic_val = new Set() // Объявляем пустой set
        // Создаём set из уникальных значений в сортируемом столбце. Они уже упорядочены.
        for (var i = 0; i < rows.length; i++) {
            unic_val.add(rows[i][column_index])
        }
        // Для элементов в set:
        //   Объявляем пустое множество 2
        //   Для строк в исходной коллекции rows:
        //      если значение в ячейке с номером сортируемого столбца строки Y равно уникальному значению I:
        //          добавить строку во множество 2
        for (var i = 0; i < unic_val.length; i++) {
            var tmp_rows = []
            for (var y = 0; y < rows.length; y++) {
                if (rows[y][column_index] == unic_val[i]) {
                    tmp_rows.push(rows[y])
                }
            }
            // Проверяем индекс колонки
            // Check the index of column
            if (column_index == 4) {
                // Сортируем строки с одинаковым значением той же функцией, что и всю таблицу
                // Sort rows with identical value in rows[y][column_index]
                tmp_rows.sort(function (a, b) {
                var x = getValue((reverse ? a : b).cells[5])
                var y = getValue((reverse ? b : a).cells[5])
                return isNaN(x - y) ? x.localeCompare(y) : x - y
                })
            } else {}
            // Добавляем вторично отсортированные строки во множество 1
            // Extend new_rows
            Array.prototype.push.apply(new_rows, tmp_rows)
        }
        // Требуемое множество строк равно вторично отсортированному
        rows = new_rows

        // Make a clone without content
        var clone_tbody = org_tbody.cloneNode()

        // Build a sorted table body and replace the old one.
        while (rows.length) {
          clone_tbody.appendChild(rows.splice(0, 1)[0])
        }

        // And finally insert the end result
        table.replaceChild(clone_tbody, org_tbody)
      }
    } catch (error) {
      // console.log(error)
    }
  }
})


Ответы (1 шт):

Автор решения: LikeAtl

При помощи column.log() и консоли в режиме разработчика браузера разобрался, где не передаются данные. Погуглил, как правильно, и исправил. Теперь код может выполнять вторичную сортировку по требуемым столбцам.

Для сравнения приведу НЕВЕРНЫЙ код моей дополнительной сортировки:

    var new_rows = [] // Объявляем пустое множество 1
    var unic_val = new Set() // Объявляем пустой set
    // Создаём set из уникальных значений в сортируемом столбце. Они уже упорядочены.
    for (var i = 0; i < rows.length; i++) {
        unic_val.add(rows[i][column_index])
    }
    // Для элементов в set:
    //   Объявляем пустое множество 2
    //   Для строк в исходной коллекции rows:
    //      если значение в ячейке с номером сортируемого столбца строки Y равно уникальному значению I:
    //          добавить строку во множество 2
    for (var i = 0; i < unic_val.length; i++) {
        var tmp_rows = []
        for (var y = 0; y < rows.length; y++) {
            if (rows[y][column_index] == unic_val[i]) {
                tmp_rows.push(rows[y])
            }
        }
        // Проверяем индекс колонки
        // Check the index of column
        if (column_index == 4) {
            // Сортируем строки с одинаковым значением той же функцией, что и всю таблицу
            // Sort rows with identical value in rows[y][column_index]
            tmp_rows.sort(function (a, b) {
            var x = getValue((reverse ? a : b).cells[5])
            var y = getValue((reverse ? b : a).cells[5])
            return isNaN(x - y) ? x.localeCompare(y) : x - y
            })
        } else {}
        // Добавляем вторично отсортированные строки во множество 1
        // Extend new_rows
        Array.prototype.push.apply(new_rows, tmp_rows)
    }
    // Требуемое множество строк равно вторично отсортированному
    rows = new_rows

И ВЕРНЫЙ код моей дополнительной сортировки:

    var new_rows = [] // Объявляем пустое множество 1
    var unic_val = new Set() // Объявляем пустой set

    // Создаём set из уникальных значений в сортируемом столбце. Они уже упорядочены.
    for (var i = 0; i < rows.length; i++) {
        unic_val.add(getValue(rows[i].cells[column_index]))
    }

    // Для элементов в set:
    //   Объявляем пустое множество 2
    //   Для строк в исходной коллекции rows:
    //      если значение в ячейке с номером сортируемого столбца строки Y равно уникальному значению I:
    //          добавить строку во множество 2
    for (let item of unic_val) {
        var tmp_rows = []
        for (var el = 0; el < rows.length; el++) {
            if (getValue(rows[el].cells[column_index]) == item) {
                tmp_rows.push(rows[el])
            }
        }

        // Проверяем индекс колонки. Введите свои номера вместо 4 и 5.
        // Check the index of column. Insert your numbers instead of 4 and 5.
        if (column_index == 4) {
            // Сортируем той же функцией строки с одинаковым значением в rows[el].cells[column_index], что и всю таблицу
            // Sort rows with identical value in rows[el].cells[column_index]
            tmp_rows.sort(function (a, b) {
            var x = getValue((reverse ? a : b).cells[5])
            var y = getValue((reverse ? b : a).cells[5])

            return isNaN(x - y) ? x.localeCompare(y) : x - y
            })
        } else {}

        // Добавляем вторично отсортированные строки во множество 1
        // Extend new_rows
        Array.prototype.push.apply(new_rows, tmp_rows)
    }

    // Требуемое множество строк равно вторично отсортированному
    rows = new_rows

Т.е. просто в JS собственные методы set, отличные от методов множеств, плюс значение из ячейки строки во множестве строк таблицы rows берётся через getValue(имя_множества_строк[номер_строки].cells[индекс_колонки])

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

document.addEventListener('click', function (e) {
  var down_class = ' dir-d '
  var up_class = ' dir-u '
  var regex_dir = / dir-(u|d) /
  var regex_table = /\bsortable\b/
  var element = e.target

  function reClassify(element, dir) {
    element.className = element.className.replace(regex_dir, '') + dir
  }

  function getValue(element) {
    // If you aren't using data-sort and want to make it just the tiniest bit smaller/faster
    // comment this line and uncomment the next one
    // return element.getAttribute('data-sort') || element.innerText
    return element.innerText
  }

  if (element.nodeName === 'TH') {
    try {
      var tr = element.parentNode
      // var table = element.offsetParent; // Fails with positioned table elements
      // this is the only way to make really, really sure. A few more bytes though... ??
      var table = tr.parentNode.parentNode
      if (regex_table.test(table.className)) {
        var column_index
        var nodes = tr.cells

        // reset thead cells and get column index
        for (var i = 0; i < nodes.length; i++) {
          if (nodes[i] === element) {
            column_index = i
          } else {
            reClassify(nodes[i], '')
          }
        }

        var dir = down_class

        // check if we're sorting up or down, and update the css accordingly
        if (element.className.indexOf(down_class) !== -1) {
          dir = up_class
        }

        reClassify(element, dir)

        // extract all table rows, so the sorting can start.
        var org_tbody = table.tBodies[0]

        // get the array rows in an array, so we can sort them...
        var rows = [].slice.call(org_tbody.rows, 0)

        var reverse = dir === up_class

        // sort them using custom built in array sort.
        rows.sort(function (a, b) {
          var x = getValue((reverse ? a : b).cells[column_index])
          var y = getValue((reverse ? b : a).cells[column_index])
          // var y = (reverse ? b : a).cells[column_index].innerText
          // var x = (reverse ? a : b).cells[column_index].innerText
          return isNaN(x - y) ? x.localeCompare(y) : x - y
        })

        // My additional code (without sort functions): start 
        var new_rows = [] // Объявляем пустое множество 1
        var unic_val = new Set() // Объявляем пустой set

        // Создаём set из уникальных значений в сортируемом столбце. Они уже упорядочены.
        for (var i = 0; i < rows.length; i++) {
            unic_val.add(getValue(rows[i].cells[column_index]))
        }

        // Для элементов в set:
        //   Объявляем пустое множество 2
        //   Для строк в исходной коллекции rows:
        //      если значение в ячейке с номером сортируемого столбца строки Y равно уникальному значению I:
        //          добавить строку во множество 2
        for (let item of unic_val) {
            var tmp_rows = []
            for (var el = 0; el < rows.length; el++) {
                if (getValue(rows[el].cells[column_index]) == item) {
                    tmp_rows.push(rows[el])
                }
            }

            // Проверяем индекс колонки. Введите свои номера вместо 4 и 5.
            // Check the index of column. Insert your numbers instead of 4 and 5.
            if (column_index == 4) {
                // Сортируем той же функцией строки с одинаковым значением в rows[el].cells[column_index], что и всю таблицу
                // Sort rows with identical value in rows[el].cells[column_index]
                tmp_rows.sort(function (a, b) {
                var x = getValue((reverse ? a : b).cells[5])
                var y = getValue((reverse ? b : a).cells[5])

                return isNaN(x - y) ? x.localeCompare(y) : x - y
                })
            } else {}

            // Добавляем вторично отсортированные строки во множество 1
            // Extend new_rows
            Array.prototype.push.apply(new_rows, tmp_rows)
        }

        // Требуемое множество строк равно вторично отсортированному
        rows = new_rows

        // My additional code (without sort functions): finish

        // Make a clone without content
        var clone_tbody = org_tbody.cloneNode()

        // Build a sorted table body and replace the old one.
        while (rows.length) {
          clone_tbody.appendChild(rows.splice(0, 1)[0])
        }

        // And finally insert the end result
        table.replaceChild(clone_tbody, org_tbody)
      }
    } catch (error) {
      // console.log(error)
    }
  }
})

→ Ссылка