如何在TypeScript中编写支持嵌套对象的通用表格排序函数?
问题描述
我有一个通过对象数组填充数据的HTML表格,表格列对应对象的属性。我希望点击任意列表头时对数据进行排序,但由于表格输入对象包含嵌套对象,难以编写通用排序函数。
示例说明
假设有一个Order接口:
export interface Order { date: string; paid: boolean; customer: { name: string; address: { city: string; } } }
我有一个列数据数组:
tableColumns: {colName: string; header: string}[] = [ {colName: 'date', header: 'Date'}, {colName: 'paid', header: 'Is paid'}, {colName: 'customer.name', header: 'Name'}, {colName: 'customer.address.city', header: 'City'} ]
表格代码如下:
<table> <thead> <th *ngFor="let col of tableColumns" (click)="sort(col.colName)">{{ col.header }}</th> </thead> <tbody *ngFor="let o of orders"> <tr> <td>{{ o.date }}</td> <td>{{ o.paid }}</td> <td>{{ o.customer.name }}</td> <td>{{ o.customer.address.city }}</td> </tr> </tbody> </table>
我的初始排序方法只能处理非嵌套属性:
sort(colName: string) { this.orders.sort((a, b) => a[colName] < b[colName] ? 1 : a[colName] > b[colName] ? -1 : 0) }
显然该方法仅对date和paid有效,无法处理嵌套对象属性。我目前用if语句做了临时解决方案,但不够优雅,请问是否有办法编写通用的排序方法?
我当前的临时解决方案(并不满意):
sort(colName: string) { if (colName === 'customer.name') { this.orders.sort((a, b) => a.customer.name < b.customer.name ? 1 : a.customer.name > b.customer.name ? -1 : 0) } else { this.orders.sort((a, b) => a[colName] < b[colName] ? 1 : a[colName] > b[colName] ? -1 : 0) } }
通用排序方案
1. 编写嵌套属性取值工具函数
先实现一个能解析点分隔路径、获取嵌套对象属性的通用函数,支持任意层级的嵌套:
getNestedValue(obj: any, path: string): any { return path.split('.').reduce((current, key) => { return current && current[key] !== undefined ? current[key] : undefined; }, obj); }
2. 改造通用排序函数
利用上面的工具函数,就能写出支持嵌套属性的排序方法,同时还可以加入升序/降序切换的优化(可选):
// 新增变量记录当前排序列和方向 currentSortCol: string = ''; sortDirection: 'asc' | 'desc' = 'asc'; sort(colName: string) { // 点击同一列时切换排序方向 if (this.currentSortCol === colName) { this.sortDirection = this.sortDirection === 'asc' ? 'desc' : 'asc'; } else { this.currentSortCol = colName; this.sortDirection = 'asc'; } this.orders.sort((a, b) => { const valA = this.getNestedValue(a, colName); const valB = this.getNestedValue(b, colName); // 针对不同类型做适配比较 if (typeof valA === 'string' && typeof valB === 'string') { return this.sortDirection === 'asc' ? valA.localeCompare(valB) : valB.localeCompare(valA); } if (typeof valA === 'boolean' && typeof valB === 'boolean') { const boolValA = valA ? 1 : -1; const boolValB = valB ? 1 : -1; return this.sortDirection === 'asc' ? boolValA - boolValB : boolValB - boolValA; } // 数字、日期等类型的比较 return this.sortDirection === 'asc' ? (valA > valB ? 1 : valA < valB ? -1 : 0) : (valB > valA ? 1 : valB < valA ? -1 : 0); }); }
3. 表头排序标识优化(可选)
如果要在表头显示当前排序方向,可修改模板:
<th *ngFor="let col of tableColumns" (click)="sort(col.colName)"> {{ col.header }} <span *ngIf="currentSortCol === col.colName"> {{ sortDirection === 'asc' ? '↑' : '↓' }} </span> </th>
方案说明
getNestedValue通过拆分路径字符串,逐层遍历对象获取属性值,完美适配customer.name、customer.address.city这类嵌套路径。- 排序函数中增加类型判断,针对字符串、布尔值等不同类型做适配比较,确保排序逻辑准确。
- 升序/降序切换属于体验优化,不需要的话可以去掉相关变量和逻辑,只保留核心的嵌套属性比较部分。
内容的提问来源于stack exchange,提问作者ma_po
相关产品推荐
相关产品推荐

