Как создать функцию для сортировки массива объектов по некоторому свойству в reactjs?

#javascript #reactjs #sorting

#javascript #reactjs #сортировка

Вопрос:

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

 array=[ 0: {id: "7", name: "Aaaaa", level: "3/4", generation: "2", area: "JavaScript", …} 1: {id: "6", name: "Bbbbbb", level: "2/4", generation: "2", area: "JavaScript", …} 2: {id: "10", name: "DEV #1", level: "1/1", generation: "1", area: "Development", …} ]
  

вот как i вызвать функцию

 sortHandle={() => this.sortArray("someProperty", "desc/asc")}
  

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

 sortCourses(sorter, asc) {
        let courses = this.state.courses;
        let sortedCourses = []
        switch (sorter) {
            case 'level':
                sortedCourses.push(courses.sort((a, b) => { return (parseInt(a.level) - parseInt(b.level)) ? 1 : -1 }))     
                break;
            case 'generation':
                sortedCourses.push(courses.sort((a, b) => { return (parseInt(a.generation) - parseInt(b.generation)) ? 1 : -1 }))
                break;
            case 'name':
                sortedCourses.push(courses.sort((a, b) => { return (a.name.toLowerCase() - b.name.toLowerCase()) ? 1 : -1 }))
            case 'area':
                sortedCourses.push(courses.sort((a, b) => { return (a.area.toLowerCase() - b.area.toLowerCase()) ? 1 : -1 }))
                break;
            case 'date':
                sortedCourses.push(courses.sort((a, b) => { return (a.startDate - b.startDate) ? 1 : -1 }))
                break;
        }
        this.setState({ courses: sortedCourses[0] }, () => { console.log(this.state.courses) })

        if (asc) {
            return sortedCourses[0]
        }
        else if (!asc) {
            return sortedCourses[0].reverse()
        }
    }
  

Я получаю sortProperty и asc / desc из props sortHandle, который является функцией в дочернем компоненте.

 sortHandle={(sorter,asc) => this.sortCourses(sorter,asc)}
  

и в моем дочернем компоненте сортировщики выглядят следующим образом:

 <span className="margin-h-10 row align-center padding-r-5 pointer"
                        onClick={() => {
                            this.setState({ asc: !this.state.asc });
                            this.props.sortHandle('name', this.state.asc)
                        }}>
                        <span className="margin-r-5">Name</span>
                        <FontAwesomeIcon
                            icon={this.state.asc ? 'chevron-up' : 'chevron-down'}
                            style={{ color: "#7f7f7f", fontSize: "10px" }}
                        />
                    </span>
                    <span className="margin-h-10 row align-center padding-r-5 pointer"
                        onClick={() => {
                            this.setState({ asc: !this.state.asc });
                            this.props.sortHandle('date', this.state.asc)
                        }}>
                        <span className="margin-r-5">Date</span>
  

Кажется, это работает нормально, но только после того, как я нажму сортировщик 2 раза. Я думаю, это как-то связано с состоянием. Есть решения?

Комментарии:

1. если вы не против использования пакета, thenby подходит для нужд сортировки

2. используйте простую сортировку: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference /… а затем задать состояние отсортированному массиву: this.setState([...array].sort(...))

3. Простая сортировка не будет работать правильно для чисел

4. Бесстыдный самопереключатель: я написал помощник для функций сортировки, который может оказаться удобным: npmjs.com/package/ts-comparer-builder

Ответ №1:

может быть, вам нужно что-то вроде этого

list Это ваш массив

key это ключ, с помощью которого вы хотите его отсортировать

и isAsc это true если вы хотите отсортировать asc или false если вы хотите отсортировать с desc

 compareStr = key => (a, b) => {
    if (a[key] < b[key]) {
        return -1
    }
    if (a[key] > b[key]) {
        return 1
    }
    return 0
}

sortArray = (list, key, isAsc) => {
    if (!list || !list.length) return []
    const duplicate = [...list]
    if (typeof duplicate[0][key] === 'number' || Number(duplicate[0][key])) {
        duplicate.sort((a, b) =>
            isAsc
                ? Number(a[key]) - Number(b[key])
                : Number(b[key]) - Number(a[key])
        )
        return duplicate
    }
    duplicate.sort(compareStr(key))
    if (isAsc) return duplicate
    return duplicate.reverse()
}
  

Это будет работать нормально, если в вашем массиве ваши числа будут числами, а не строками.
Таким образом, вы можете или изменить типы чисел, или вы можете добавить свою собственную проверку регистра чисел вместо этого if (typeof duplicate[0][key] === 'number') {

Комментарии:

1. Это очень помогло. Я изменил свой код, чтобы просто возвращать sortedArray.reverse(), если order ===’desc’, и это работает

Ответ №2:

Вот небольшая хитрость. Вам не нужно выполнять сортировку в обработчике событий — вы можете просто сделать это в своем рендеринге и сохранить только информацию о свойстве сортировки и порядке. Кроме того, вам нужно только закодировать одну версию сортировщика для каждого из свойств, просто умножив возвращаемое значение сортировщиков на -1 , если вы хотите изменить порядок на убывающий.

Сначала я бы извлек сами сортировщики:

 const sorters = {
  level: (a, b) => parseInt(a.level) - parseInt(b.level),
  generation: (a, b) => parseInt(a.generation - b.generation),
  name: (a, b) => a.name.localeCompare(b.name),
  area: (a, b) => a.area.localeCompare(b.area),
};
  

Достаточно просто. Затем я бы поместил их в ваше состояние вместе с порядком сортировки:

 const SortOrder = {
  ASC: 1,
  DESC: -1,
};

// ...

state = {
  sortOrder: SortOrder.ASC,
  sortProperty: "level",
  array: [],
  // ...
};
  

Затем в render() вы просто берете свой массив и сортируете его в соответствии с сортировщиком:

 render() {
  const { array, sortProperty, sortOrder } = this.state;
  const arraySorter = sorters[sortProperty];
  const displayArray = [...array].sort(
    (a, b) => arraySorter(a, b) * sortOrder
  );

  return (
    <>
      {displayArray.map((item) => (
        <p key={item}>{item}</p>
      ))}
    </>
  );
}
  

Обратите внимание, как мы берем правильную функцию сортировки из sorters и умножаем ее на текущую sortOrder . Это позволяет нам изменять порядок возрастания / убывания, просто меняя между 1 и -1, так как при -1 порядок меняется на обратный.

Затем, наконец, в sortArray мы просто обновляем состояние новым свойством сортировки и порядком сортировки:

 sortArray = (property, order) => {
  this.setState({
    sortProperty: property,
    sortOrder: order === "asc" ? SortOrder.ASC : SortOrder.DESC,
  });
};
  

Здесь API такой же, как у вашего, т.е. () => this.sortArray('level', 'desc') , но, честно говоря, вы могли бы просто перейти order непосредственно к sortOrder и вызвать его как () => this.sortArray('level', SortOrder.DESC) . Или полностью удалите порядок из функции, поскольку теперь вы можете изменять его отдельно от свойства.

Все вместе:

 const SortOrder = {
  ASC: 1,
  DESC: -1,
};

const sorters = {
  level: (a, b) => parseInt(a.level) - parseInt(b.level),
  generation: (a, b) => parseInt(a.generation - b.generation),
  name: (a, b) => a.name.localeCompare(b.name),
  area: (a, b) => a.area.localeCompare(b.area),
};

class MyComponent extends React.Component {
  state = {
    sortOrder: SortOrder.ASC,
    sortProperty: "level",
    array: [],
    // ...
  };

  sortArray = (property, order) => {
    this.setState({
      sortProperty: property,
      sortOrder: order === "asc" ? SortOrder.ASC : SortOrder.DESC,
    });
  };

  render() {
    const { array, sortProperty, sortOrder } = this.state;
    const arraySorter = sorters[sortProperty];
    const displayArray = [...array].sort(
      (a, b) => arraySorter(a, b) * sortOrder
    );

    return (
      <>
        {displayArray.map((item) => (
          <p key={item}>{item}</p>
        ))}
      </>
    );
  }
}
  

Комментарии:

1. Проблема с этой функцией () => this.sortArray(‘level’, ‘desc’) заключается в том, что мне нужно получить параметры функции из состояния дочернего компонента. Означает, что когда я нажимаю на некоторый сортировщик в дочернем компоненте, он запускает событие onClick, которое передает свое состояние (свойство, порядок) функции sortArray в родительском компоненте. Извините, если я не очень хорошо это объяснил. Я все еще изучаю react, и мой английский не идеален