#angular #typescript #rxjs
Вопрос:
У меня есть одержимый тип табличных данных, который возвращает мне набор строк и столбцов таблицы. Пользователь может выбрать из списка выбрать набор столбцов (к которым относятся строки) для сортировки (по умолчанию в порядке возрастания). Например, если пользователь вводит «сотрудник» и «проект», я должен сначала отсортировать строки по сотруднику и, в свою очередь, отсортировать проекты в порядке возрастания для каждого сотрудника.
тип класса:
export interface TableColumn {
value: string;
label: string;
hidden: boolean;
format: (value: any) => string;
}
export interface TableRow {
[key: string]: any;
}
export interface TableData {
columns: TableColumn[];
rows: TableRow[];
}
export class Activity {
id?: number;
[HOURS_KEY]: number;
[DATE_KEY]: string;
[EMPLOYEE_KEY]: ActivityEmployee;
[TYPE_KEY]: ActivityType;
[PROJECT_KEY]?: ActivityProject;
}
export class ActivityEmployee {
id: number;
lastName: string;
firstName: string;
}
Предполагая, что теперь я хотел отсортировать только по сотрудникам и проектам (так что это статический пример), как я могу упорядочить по цепочке все, что я получаю?
я называю навязчивый результат таким образом:
tableDataColumns$ = this.activityCollection.groupedEntities$?.pipe(
map((e) => e.columns)
);
tableDataRows$ = this.activityCollection.groupedEntities$?.pipe(
map((e) => e.rows)
);
Ответ №1:
sortBy = new Subject<[string, string]>();
tableDataRows$ = this.sortBy.pipe(
startWith(["", ""]),
switchMap(([sort1, sort2]) =>
this.activityCollection.groupedEntities$.pipe(
map(({ rows }) => rows),
map(rows =>
!!sort1 ? rows.sort((a, b) => (a[sort1] < b[sort1] ? -1 : 1)) : rows
),
map(rows => (!!sort2 ? this.splitSortRows(rows, sort1, sort2) : rows))
)
)
);
splitSortRows = <T>(rows: T[], splitBy: string, sortBy: string): T[] => {
const uniqueValues = [...new Set(rows.map(row => row[splitBy]))];
const splitArray = uniqueValues.map(value =>
rows.filter(row => row[splitBy] === value)
);
return splitArray.reduce(
(acc, arrSegment) => [
...acc,
...arrSegment.sort((a, b) => a[sortBy] < b[sortBy] ? -1 : 1),
],
[] as T[]
);
};
Мы начинаем с объявления темы, которая будет выдавать кортеж, представляющий две метки для сортировки. Затем мы настроим нашу трубу в следующей последовательности:
- Подпишитесь на нашу тему, чтобы получать последние критерии сортировки
- Переключите карту на наши наблюдаемые исходные данные.
- Используйте
map()
с деконструкцией объектов, чтобы получить нашиrows
данные. - Сортировка
rows
массива на основе наших первых критериев, выполненная с помощью простого.sort()
. - Затем мы вызываем более сложную функцию для выполнения субсортировки.
Что касается того, как splitSortRows()
работает:
- Создайте массив уникальных значений строк по первому критерию сортировки.
- Для каждого уникального элемента мы разбиваем наш единый массив на несколько массивов. Каждая группа подмассивов имеет одинаковое значение наших первых критериев фильтрации.
- Затем мы сортируем каждый поддиапазон, а затем объединяем их обратно в один массив.
Теперь, когда ваш пользователь нажимает на одну или две метки для сортировки, вы можете указать эти метки сортировки в sortBy
теме. Подписка позаботится обо всем остальном.
this.sortBy.next(['employee', 'project']);
// tableDataRows$ should now emit a new array of rows sorted by employee first, and projects second
Комментарии:
1. спасибо, но у меня 5 столбцов(не все числовые, некоторые строковые), так что этот подход, возможно, слишком широк
2. Извини за это @Man24. Я обновил свой ответ, чтобы он сортировал как числа, так и строки.