通过添加3个相对简单的帮助程序,可以构建一个非常直观的功能解决方案。在深入研究之前,我们先来了解一下用法:
function usage(homes, { asc, desc, fallback }) {
homes.sort(fallback(
asc(home => home.city),
desc(home => parseInt(home.price, 10)),
));
console.log(homes);
}
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",
}];
const SortHelpers = (function () {
const asc = (fn) => (a, b) => (a = fn(a), b = fn(b), -(a < b) || +(a > b));
const desc = (fn) => (a, b) => asc(fn)(b, a);
const fallback = (...fns) => (a, b) => fns.reduce((diff, fn) => diff || fn(a, b), 0);
return { asc, desc, fallback };
})();
usage(homes, SortHelpers);
如果你向下滚动代码片段,你可能已经看到了helper:
const asc = (fn) => (a, b) => (a = fn(a), b = fn(b), -(a < b) || +(a > b));
const desc = (fn) => (a, b) => asc(fn)(b, a);
const fallback = (...fns) => (a, b) => fns.reduce((diff, fn) => diff || fn(a, b), 0);
让我快速解释一下这些函数的作用。
asc creates a comparator function. The provided function fn is called for both the comparator arguments a and b. The results of the two function calls are then compared. -1 is returned if resultA < resultB, 1 is returned if resultA > resultB, or 0 otherwise. These return values correspond with an ascending order direction.
It could also be written like this:
function asc(fn) {
return function (a, b) {
// apply `fn` to both `a` and `b`
a = fn(a);
b = fn(b);
if (a < b) return -1;
if (a > b) return 1;
return 0;
// or `return -(a < b) || +(a > b)` for short
};
}
desc is super simple, since it just calls asc but swaps the a and b arguments, resulting in descending order instead of ascending.
fallback (there might be a better name for this) allows us to use multiple comparator functions with a single sort.
Both asc and desc can be passed to sort by themself.
homes.sort(asc(home => home.city))
There is however an issue if you want to combine multiple comparator functions. sort only accepts a single comparator function. fallback combines multiple comparator functions into a single comparator.
The first comparator is called with arguments a and b, if the comparator returns the value 0 (meaning that the values are equal) then we fall back to the next comparator. This continues until a non-0 value is found, or until all comparators are called, in which case the return value is 0.
也可以为fallback()提供自定义比较器函数。假设您想使用localeCompare()而不是比较字符串与<和>。在这种情况下,您可以将asc(home => home.city)替换为(a, b) => a.city.localeCompare(b.city)。
homes.sort(fallback(
(a, b) => a.city.localeCompare(b.city),
desc(home => parseInt(home.price, 10)),
));
需要注意的一点是,在与<和>比较时,未定义的值总是返回false。因此,如果一个值可能缺失,您可能希望首先根据它的存在进行排序。
homes.sort(fallback(
// homes with optionalProperty first, true (1) > false (0) so we use desc
desc(home => home.optionalProperty != null), // checks for both null and undefined
asc(home => home.optionalProperty),
// ...
))
因为用localeCompare()比较字符串是一件很常见的事情,所以可以将其作为asc()的一部分。
function hasMethod(item, methodName) {
return item != null && typeof item[methodName] === "function";
}
function asc(fn) {
return function (a, b) {
a = fn(a);
b = fn(b);
const areLocaleComparable =
hasMethod(a, "localeCompare") && hasMethod(b, "localeCompare");
if (areLocaleComparable) return a.localeCompare(b);
return -(a < b) || +(a > b);
};
}