Таблица может быть отсортирована по алфавиту, но не в обратном алфавитном порядке

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

Однако я ожидал, что он будет отсортировать его в обратном порядке по алфавиту при втором щелчке по нему и вернется в исходное состояние при третьем щелчке.

В настоящее время он работает только при первом щелчке - он сортирует его в алфавитном порядке - но после этого он ничего не делает, независимо от того, сколько раз он был нажат.

import { Table } from 'semantic-ui-react';

export default class GenericTable extends React.PureComponent {
  constructor(props) {
    super(props);

    this.state = { 
      currentSort: 'default',
      rows: this.props.rows, // the original rows are saved in state
    };
  }

  onSortChange = index => {
    let nextSort;

    const newRows = this.props.rows.sort(function(a, b) {
      if (a.cells[index] < b.cells[index]) {
        nextSort = 'up';
        return -1;
      }
      if (a.cells[index] > b.cells[index]) {
        nextSort = 'default';
        return 1;
      }
      nextSort = 'down';
      return 0;
    });
    this.setState({ rows: newRows, currentSort: nextSort });
  };

  render() {

    const { currentSort } = this.state; // added state
    const sortTypes = { // added constant
      up: {
       class: 'sort-up',
       fn: (a, b) => a.name - b.name,
      },
      down: {
        class: 'sort-down',
        fn: (a, b) => b.name - a.name,
      },
     default: {
       class: 'sort',
       fn: (a, b) => a,
     },
   };
    const { headers, rows, idList } = this.props;
    
    return (
      <Table>
        <Table.Header>
          <Table.Row>
             {headers.map(header => (
                <Table.HeaderCell key={headers.indexOf(header)}>
                 {header}
                 // added button
                 <button onClick={() => this.onSortChange(index)} type="button">
                   <i className={`fas fa-${sortTypes[currentSort].class}`} />
                 </button>
                </Table.HeaderCell>
             )}
          </Table.Row>
        </Table.Header>

        <Table.Body>
         // added below
         {rows.map((row, rowIndex) => (
            <Table.Row key={idList && idList[rowIndex]}>
                <Table.Cell>
                  ...
                </Table.Cell>
         
            </Table.Row>
          )}
        </Table.Body>
      </Table>
    );
  }
}

Я предполагаю, что что-то не так в onSortChange, но не знаю что.


person Leo Messi    schedule 06.08.2020    source источник


Ответы (2)


Сортировка Javascript, возможно, не так проста, как кажется. Я пытаюсь объяснить вам это, чтобы вы могли исправить свою функцию.

Предположим, у нас есть эти значения:

var items = [
  { name: 'Edward', value: 21 },
  { name: 'Sharpe', value: 37 },
  { name: 'And', value: 45 },
  { name: 'The', value: -12 },
  { name: 'Magnetic', value: 13 },
  { name: 'Zeros', value: 37 }
];

И пишем эту функцию:

items.sort()

мало что происходит. Это потому, что мы имеем дело со списком объектов и поэтому хотим добавить compareFunction. Вы сделали это довольно хорошо, но я полагаю, вы не совсем понимаете, что он делает. Если мы используем это compareFunction, например:

items.sort(function (a, b) {
  var nameA = a.name.toUpperCase(); // ignore upper/lower case!
  var nameB = b.name.toUpperCase(); // ignore upper/lower case!
  if (nameA < nameB) {
    return -1;
  }
  if (nameA > nameB) {
    return 1;
  }

  // Namen müssen gleich sein
  return 0;
});

Наш список будет отсортирован по алфавиту. Почему? Потому что мы говорим о функции прямо здесь:

var nameA = a.name.toUpperCase(); // ignore upper/lower case!
var nameB = b.name.toUpperCase(); // ignore upper/lower case!

что мы ищем свойства имени нашего объекта. Мы могли бы легко заменить a.name.toUpperCase() на a.value, и теперь мы будем сортировать наш массив по значению внутри свойства value нашего объекта.

При этом ваша функция сортировки в настоящее время запрограммирована на сортировку в алфавитном порядке (a - z). Вы никогда не реализовывали логику, чтобы сделать обратное или прекратить сортировку. Я думаю, вы, вероятно, предположили, что compareFunction уже выполняет функцию переключения сортировки, но это не так.

Чтобы выполнить сортировку по убыванию (z - a), вы можете использовать функцию .reverse (). Это будет выглядеть примерно так:

items.sort(function (a, b) {
  var nameA = a.name.toUpperCase(); // ignore upper/lower case!
  var nameB = b.name.toUpperCase(); // ignore upper/lower case!
  if (nameA < nameB) {
    return -1;
  }
  if (nameA > nameB) {
    return 1;
  }

  // Namen müssen gleich sein
  return 0;
}).reverse();

et voilà - ваш список теперь отсортирован в порядке убывания (z - a). (подробнее читайте здесь: Сортировка строк в порядке убывания в Javascript (большинство эффективно)?).

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

подробнее о сортировке массивов (документы javascript: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)

person Tim Gerhard    schedule 06.08.2020

onSortChange = index => {
  const sortMap = {
    default: 'up',
    up: 'down',
    down: 'default',
  };

  let { currentSort, currentIndex } = this.state;

  let nextSort = currentIndex === index ? sortMap[currentSort] : 'default';

  let newRows = [...this.props.rows];

  switch (nextSort) {
    case 'up':
      newRows.sort((a, b) => (a.cells[index] <= b.cells[index] ? -1 : 1));
      break;
    case 'down':
      newRows.sort((a, b) => (b.cells[index] <= a.cells[index] ? -1 : 1));
      break;
  }

  this.setState({ rows: newRows, currentSort: nextSort, currentIndex: index });
};

Во-первых, не следует вызывать метод sort непосредственно на props.rows. Потому что он напрямую изменит props.rows вместо того, чтобы возвращать отсортированный новый массив. Итак, мы сначала получаем копию исходных данных через [...this.props.rows], а затем сортируем ее.

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

Наконец, чтобы не повлиять на последний результат сортировки при нажатии на другой столбец. Мы добавили индекс последнего отсортированного столбца в state. Если индексы двух последних отсортированных столбцов не совпадают, используйте порядок up для прямой сортировки.

person Sweet Liquid    schedule 07.08.2020
comment
Я использовал ваш метод, но получаю сообщение об ошибке для строки в состоянии, в котором определены строки: this.state = { rows: this.props.rows, ... }, а также для последней строки в onSortChange методе, где this.setState({ rows: newRows, ... }). Сообщение об ошибке: Поле неиспользуемого состояния: «строки» реагируют / нет неиспользованного состояния. Есть идеи, почему это появляется? - person Leo Messi; 07.08.2020