背景
在开发过程中,对象数组的排序会经常用到。如果想要实现多个字段按优先级排序的话,如何处理?这种情况比较少见。举例来说,有以下对象数组:
const list = [
{
"title": "This is a deme page",
"name": "Aborn Jiang",
"age": 21
},
{
"title": "mindpress is a good framework",
"name": "John",
"age": 31
},
{
"title": "Goodness",
"name": "Aborn Jiang",
"age": 19
}
]
如果我们想按 name
字段排序,当name
字段相同时,再按title
字段排序。
放得更加宽泛点,可以按任意多字段排序。
单字段排序
如果只按name
排序,那调用sort
方法如下:
const sortedList = list.sort((a, b) =>
a.name.localeCompare(b.name)
)
两字段排序
如果先按name
排序,在name
相同的情况下,再按 age
排序,写法台下:
const sortedList = list.sort((a, b) =>
a.name.localeCompare(b.name) || a.age - b.age
)
通用多字段排序
由两字段再扩展到任意字段,其实是我们需要分别判断每个字段,当前一个字段比较后不为 0,则返回;如果为 0表示继续采用后一个字段进行排序,通用写法如下:
export interface SortFields {
[field: string]: -1 | 1
}
export type SortOptions = SortFields
export const get = (obj: any, path: string): any => path.split('.').reduce((acc, part) => acc && acc[part], obj)
/**
* Sort list of items by givin options
* sort array of object by multi fields.
* example:
* const sortedArray = sortList(toBeSortedArray, { 'createTime': -1, 'title': 1 })
*/
export const sortList = (data: any[], params: SortOptions) => {
const comperable = new Intl.Collator(params.$locale as string, {
numeric: params.$numeric as boolean,
caseFirst: params.$caseFirst as any,
sensitivity: params.$sensitivity as any
})
const keys = Object.keys(params)
data = data.sort((a, b) => {
let res = 0;
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
const values = [get(a, key), get(b, key)]
.map((value) => {
// `null` values are treated as `"null"` strings and ordered alphabetically
// Turn `null` values into `undefined` so they place at the end of the list
if (value === null) {
return undefined
}
// Convert Date object to ISO string
if (value instanceof Date) {
return value.toISOString()
}
return value
})
if (params[key as keyof SortOptions] === -1) {
values.reverse()
}
res = comperable.compare(values[0], values[1])
if (res != 0) return res;
}
return res;
})
return data
}
调用举例:
sortedArray = sortList(toBeSortedArray, { 'createTime': -1, 'title': 1 })
先按 createTime
从最新到最旧排序(逆序,所以用-1
),在时间相同的情况下,使用title
排序(正序,用1
)
代码见: https://github.com/aborn/mindpress/blob/main/mindpress-fe/server/utils/query/sort.ts