【发布时间】:2017-06-24 20:17:54
【问题描述】:
有没有办法让 Lodash 的 orderBy 函数支持重音字符?
如á、é、ñ等。在执行排序时,它们会移到数组的末尾。
【问题讨论】:
标签: javascript lodash
有没有办法让 Lodash 的 orderBy 函数支持重音字符?
如á、é、ñ等。在执行排序时,它们会移到数组的末尾。
【问题讨论】:
标签: javascript lodash
听起来它不使用localeCompare,而是默认使用< 或>,它通过UTF-16 代码单元数值进行比较,而不是区域设置排序(排序)。
您可以转换为数组(如果需要),然后将原生 sort 与 localeCompare 一起使用。例如,而不是:
const result = _.orderBy(theArray, ["value"]);
你可以这样做:
const result = theArray.slice().sort((a, b) => a.value.localeCompare(b.value));
或就地排序:
theArray.sort((a, b) => a.value.localeCompare(b.value));
localeCompare 对默认语言环境使用默认排序规则。使用Intl.Collator,您可以更好地控制排序规则(如不区分大小写、重音处理、大小写字符的相对位置等)。例如,如果您想要默认语言环境的默认排序规则,但首先使用大写字符:
const collator = new Intl.Collator(undefined, {caseFirst: "upper"});
const result = theArray.slice().sort((a, b) => collator.compare(a.value, b.value));
现场示例:
const theArray = [
{value: "n"},
{value: "N"},
{value: "ñ"},
{value: "á"},
{value: "A"},
{value: "a"},
];
const lodashResult = _.orderBy(theArray, ["value"]);
const localeCompareResult = theArray.slice().sort((a, b) => a.value.localeCompare(b.value));
const collator = new Intl.Collator(undefined, {caseFirst: "upper"});
const collatorResult = theArray.slice().sort((a, b) => collator.compare(a.value, b.value));
show("unsorted:", theArray);
show("lodashResult:", lodashResult);
show("localeCompareResult:", localeCompareResult);
show("collatorResult:", collatorResult);
function show(label, array) {
console.log(label, "[");
for (const element of array) {
console.log(` ${JSON.stringify(element)}`);
}
console.log("]");
}
.as-console-wrapper {
max-height: 100% !important;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>
当我第一次写这个答案时,_.orderBy 和原生 sort 之间存在细微差别:_.orderBy,如 _.sortBy,总是进行稳定排序,而在原始答案的时间 JavaScript 的原生 sort 不能保证是稳定的。不过,从那时起,JavaScript specification 已被修改为需要稳定的排序 (ES2019)。所以_.orderBy/_.sortBy和原生sort现在都稳定了。
如果“stable”与“unstable”排序不是熟悉的术语:“stable”排序是指两个在排序目的上被认为是等价的元素,保证相对于彼此保持在相同的位置;在“不稳定”排序中,它们相对于彼此的位置可能会被交换(这是允许的,因为它们对于排序目的是“等效的”)。考虑这个数组:
const theArray = [
{value: "x", id: 27},
{value: "z", id: 14},
{value: "x", id: 12},
];
如果您进行不稳定排序,仅对value 进行升序排序(忽略id 或对象可能具有的任何其他属性),则有两个有效结果:
// Valid result 1: id = 27 remained in front of id = 12
[
{value: "x", id: 27},
{value: "x", id: 12},
{value: "z", id: 14},
]
// Valid result 2: id = 27 was moved after id = 12
[
{value: "x", id: 12},
{value: "x", id: 27},
{value: "z", id: 14},
]
但是,对于稳定排序,只有第一个结果是有效的;等价元素相对于彼此的位置保持不变。
但同样,这种区别不再重要,因为 JavaScript 的 sort 现在也很稳定。
【讨论】:
_.orderBy/_.sortBy 保证排序稳定,而 JavaScript 的 sort 则没有(允许不稳定)。但是 ES2019 更改了 JavaScript 规范以要求稳定排序,因此不再相关。 (所有主要的 JavaScript 引擎都已经实现了一个稳定的排序 IIRC,因此规范更改实际上并没有改变他们所做的,只是记录了它并要求其他不太流行的实现。)我已经更新了答案以解释这一点并简要解释稳定与不稳定的排序。
我已经通过比较净化过的元素解决了这个问题。
theArray.sort(function(a, b) {
return a.toLowerCase().removeAccents().localeCompare(b.toLowerCase().removeAccents());
});
removeAccents 函数:
String.prototype.removeAccents = function () {
return this
.replace(/[áàãâä]/gi,"a")
.replace(/[éè¨ê]/gi,"e")
.replace(/[íìïî]/gi,"i")
.replace(/[óòöôõ]/gi,"o")
.replace(/[úùüû]/gi, "u")
.replace(/[ç]/gi, "c")
.replace(/[ñ]/gi, "n")
.replace(/[^a-zA-Z0-9]/g," ");
}
【讨论】: