#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, и мой английский не идеален