从这个最初的问题,我将如何在多个字段应用排序?
使用这种稍作调整的结构,我将如何排序城市(上升)和价格(下降)?
var homes = [
{"h_id":"3",
"city":"Dallas",
"state":"TX",
"zip":"75201",
"price":"162500"},
{"h_id":"4",
"city":"Bevery Hills",
"state":"CA",
"zip":"90210",
"price":"319250"},
{"h_id":"6",
"city":"Dallas",
"state":"TX",
"zip":"75000",
"price":"556699"},
{"h_id":"5",
"city":"New York",
"state":"NY",
"zip":"00010",
"price":"962500"}
];
我喜欢的事实是,给出的答案提供了一个一般的方法。在我计划使用这段代码的地方,我将不得不对日期以及其他东西进行排序。“启动”对象的能力似乎很方便,如果不是有点麻烦的话。
我试图把这个答案构建成一个很好的通用示例,但我运气不太好。
这是一个递归算法,按多个字段排序,同时有机会在比较之前格式化值。
var data = [
{
"id": 1,
"ship": null,
"product": "Orange",
"quantity": 7,
"price": 92.08,
"discount": 0
},
{
"id": 2,
"ship": "2017-06-14T23:00:00.000Z".toDate(),
"product": "Apple",
"quantity": 22,
"price": 184.16,
"discount": 0
},
...
]
var sorts = ["product", "quantity", "ship"]
// comp_val formats values and protects against comparing nulls/undefines
// type() just returns the variable constructor
// String.lower just converts the string to lowercase.
// String.toDate custom fn to convert strings to Date
function comp_val(value){
if (value==null || value==undefined) return null
var cls = type(value)
switch (cls){
case String:
return value.lower()
}
return value
}
function compare(a, b, i){
i = i || 0
var prop = sorts[i]
var va = comp_val(a[prop])
var vb = comp_val(b[prop])
// handle what to do when both or any values are null
if (va == null || vb == null) return true
if ((i < sorts.length-1) && (va == vb)) {
return compare(a, b, i+1)
}
return va > vb
}
var d = data.sort(compare);
console.log(d);
如果a和b相等,它将尝试下一个字段,直到没有可用字段。
另一种方式
var homes = [
{"h_id":"3",
"city":"Dallas",
"state":"TX",
"zip":"75201",
"price":"162500"},
{"h_id":"4",
"city":"Bevery Hills",
"state":"CA",
"zip":"90210",
"price":"319250"},
{"h_id":"6",
"city":"Dallas",
"state":"TX",
"zip":"75000",
"price":"556699"},
{"h_id":"5",
"city":"New York",
"state":"NY",
"zip":"00010",
"price":"962500"}
];
function sortBy(ar) {
return ar.sort((a, b) => a.city === b.city ?
b.price.toString().localeCompare(a.price) :
a.city.toString().localeCompare(b.city));
}
console.log(sortBy(homes));
这是一个通用的多维排序,允许在每个层次上进行反转和/或映射。
用Typescript编写。对于Javascript,请查看这个JSFiddle
的代码
type itemMap = (n: any) => any;
interface SortConfig<T> {
key: keyof T;
reverse?: boolean;
map?: itemMap;
}
export function byObjectValues<T extends object>(keys: ((keyof T) | SortConfig<T>)[]): (a: T, b: T) => 0 | 1 | -1 {
return function(a: T, b: T) {
const firstKey: keyof T | SortConfig<T> = keys[0];
const isSimple = typeof firstKey === 'string';
const key: keyof T = isSimple ? (firstKey as keyof T) : (firstKey as SortConfig<T>).key;
const reverse: boolean = isSimple ? false : !!(firstKey as SortConfig<T>).reverse;
const map: itemMap | null = isSimple ? null : (firstKey as SortConfig<T>).map || null;
const valA = map ? map(a[key]) : a[key];
const valB = map ? map(b[key]) : b[key];
if (valA === valB) {
if (keys.length === 1) {
return 0;
}
return byObjectValues<T>(keys.slice(1))(a, b);
}
if (reverse) {
return valA > valB ? -1 : 1;
}
return valA > valB ? 1 : -1;
};
}
用法示例
先按姓排序,再按名排序:
interface Person {
firstName: string;
lastName: string;
}
people.sort(byObjectValues<Person>(['lastName','firstName']));
按语言代码的名称排序,而不是按语言代码排序(见地图),然后按降序排序(见反向)。
interface Language {
code: string;
version: number;
}
// languageCodeToName(code) is defined elsewhere in code
languageCodes.sort(byObjectValues<Language>([
{
key: 'code',
map(code:string) => languageCodeToName(code),
},
{
key: 'version',
reverse: true,
}
]));