如何在JavaScript字符串排序

我有一个对象列表,我希望基于类型为字符串的字段attr进行排序。我尝试使用-

list.sort(function (a, b) {
return a.attr - b.attr
})

但发现-似乎不能处理JavaScript中的字符串。我如何能排序基于类型字符串的属性的对象列表?

578107 次浏览

在你的例子中使用String.prototype.localeCompare a:

list.sort(function (a, b) {
return ('' + a.attr).localeCompare(b.attr);
})

我们强制a.attr为字符串以避免异常。localeCompare已经支持Internet Explorer 6和Firefox 1。你可能还会看到下面的代码不尊重区域设置:

if (item1.attr < item2.attr)
return -1;
if ( item1.attr > item2.attr)
return 1;
return 0;

你应该使用>或<和==这里。所以解决方案是:

list.sort(function(item1, item2) {
var val1 = item1.attr,
val2 = item2.attr;
if (val1 == val2) return 0;
if (val1 > val2) return 1;
if (val1 < val2) return -1;
});

我为这个问题困扰了很久,所以我最终研究了这个问题,并给了你一个冗长的原因,为什么事情是这样的。

规范:

Section 11.9.4   The Strict Equals Operator ( === )


The production EqualityExpression : EqualityExpression === RelationalExpression
is evaluated as follows:
- Let lref be the result of evaluating EqualityExpression.
- Let lval be GetValue(lref).
- Let rref be the result of evaluating RelationalExpression.
- Let rval be GetValue(rref).
- Return the result of performing the strict equality comparison
rval === lval. (See 11.9.6)

现在我们看11.9.6

11.9.6   The Strict Equality Comparison Algorithm


The comparison x === y, where x and y are values, produces true or false.
Such a comparison is performed as follows:
- If Type(x) is different from Type(y), return false.
- If Type(x) is Undefined, return true.
- If Type(x) is Null, return true.
- If Type(x) is Number, then
...
- If Type(x) is String, then return true if x and y are exactly the
same sequence of characters (same length and same characters in
corresponding positions); otherwise, return false.

就是这样。应用于字符串的三重等号运算符如果参数是完全相同的字符串(相同的长度和对应位置的相同字符)则返回true。

因此,当我们试图比较可能来自不同来源的字符串,但我们知道它们最终将具有相同的值时,===将在这种情况下工作——这是代码中内联字符串的一个足够常见的场景。例如,如果我们有一个名为connection_state的变量,并且我们希望知道['connecting', 'connected', 'disconnecting', 'disconnected']现在处于以下哪一种状态,我们可以直接使用===

但还有更多。就在11.9.4上面,有一个简短的说明:

NOTE 4
Comparison of Strings uses a simple equality test on sequences of code
unit values. There is no attempt to use the more complex, semantically oriented
definitions of character or string equality and collating order defined in the
Unicode specification. Therefore Strings values that are canonically equal
according to the Unicode standard could test as unequal. In effect this
algorithm assumes that both Strings are already in normalized form.

嗯。现在该做什么?外部获得的字符串可以,而且很可能会是奇怪的unicodey,而我们温和的===不会公正地对待它们。接下来是localeCompare来拯救:

15.5.4.9   String.prototype.localeCompare (that)
...
The actual return values are implementation-defined to permit implementers
to encode additional information in the value, but the function is required
to define a total ordering on all Strings and to return 0 when comparing
Strings that are considered canonically equivalent by the Unicode standard.

我们现在可以回家了。

tl,博士;

要比较javascript中的字符串,使用localeCompare;如果你知道字符串没有非ascii组件,因为它们是,例如,内部程序常量,那么===也可以工作。

在第一个问题的操作中,您正在执行以下操作:

item1.attr - item2.attr

所以,假设这些是数字(即item1。Attr = "1", item2。attr =" 2")您仍然可以使用"==="运算符(或其他严格求值符),只要您确保类型。以下方法应该有效:

return parseInt(item1.attr) - parseInt(item2.attr);

如果它们是字母和数字,那么使用localCompare()。

更新答案(2014年10月)

我真的很讨厌这个字符串自然排序顺序,所以我花了一些时间来调查这个问题。我希望这能有所帮助。

长话短说

localeCompare()字符支持是坏蛋,使用它。 正如Shog9所指出的,你的问题的答案是:

return item1.attr.localeCompare(item2.attr);

在所有自定义javascript“自然字符串排序顺序”实现中发现的错误

有相当多的自定义实现,试图进行字符串比较更精确地称为“自然字符串排序顺序”

当“玩”这些实现时,我总是注意到一些奇怪的“自然排序顺序”选择,或者说是错误(最好的情况下是遗漏)。

通常,特殊字符(空格、破折号、&号、括号等)不会被正确处理。

然后你会发现它们出现在不同的地方,通常情况下可能是:

  • 有些介于大写字母Z和小写字母a之间
  • 有些介于9和大写的A之间
  • 有些会在小写z后面

当人们期望所有特殊字符都“分组”在一个地方时,除了空格特殊字符maybe(它总是第一个字符)。也就是说,所有字符都在数字之前,或者所有字符都在数字和字母之间(小写&大写字母“together”一个接一个),或者都在字母后面。

我的结论是,当我开始添加几乎不寻常的字符时,它们都不能提供一致的顺序。带有变音符或字符的字符,如破折号、感叹号等)。

自定义实现研究:

浏览器的原生“自然字符串排序顺序”通过localeCompare()实现

localeCompare()最老的实现(没有locale和options参数)被IE6+支持,参见http://msdn.microsoft.com/en-us/library/ie/s4esdbwz (v = vs.94) . aspx(向下滚动到localeCompare()方法)。 内置的localeCompare()方法在排序方面做得更好,甚至是国际&特殊字符。 使用localeCompare()方法的唯一问题是“使用的地区和排序顺序完全依赖于实现”。换句话说,当使用localeCompare如stringOne.localeCompare(stringTwo): Firefox, Safari, Chrome &IE对字符串有不同的排序顺序。

浏览器原生实现研究:

“字符串自然排序顺序”的难点

实现一个可靠的算法(意思是:一致但也覆盖广泛的字符)是一项非常艰巨的任务。UTF8包含超过2000个字符 &涵盖超过120个脚本(语言)。 最后,这个任务有一些规范,它被称为“Unicode Collation Algorithm”,可以在http://www.unicode.org/reports/tr10/找到。你可以找到关于这个问题的更多信息,我发布了https://softwareengineering.stackexchange.com/questions/257286/is-there-any-language-agnostic-specification-for-string-natural-sorting-order

最终结论

因此,考虑到目前我所遇到的javascript自定义实现所提供的支持级别,我们可能永远不会看到任何接近支持所有这些字符的东西&脚本(语言)。因此,我宁愿使用浏览器的本地localeCompare()方法。是的,它确实有在不同浏览器之间不一致的缺点,但基本测试表明,它涵盖了更广泛的字符范围,允许可靠的&有意义的排序顺序。

所以正如Shog9所指出的,你的问题的答案是:

return item1.attr.localeCompare(item2.attr);

进一步阅读:

多亏了Shog9的回答,让我找到了我相信的“正确”方向

list.sort(function(item1, item2){
return +(item1.attr > item2.attr) || +(item1.attr === item2.attr) - 1;
})

他们如何工作样本:

+('aaa'>'bbb')||+('aaa'==='bbb')-1
+(false)||+(false)-1
0||0-1
-1


+('bbb'>'aaa')||+('bbb'==='aaa')-1
+(true)||+(false)-1
1||0-1
1


+('aaa'>'aaa')||+('aaa'==='aaa')-1
+(false)||+(true)-1
0||1-1
0

答案(现代ECMAScript)

list.sort((a, b) => (a.attr > b.attr) - (a.attr < b.attr))

list.sort((a, b) => +(a.attr > b.attr) || -(a.attr < b.attr))

描述

将一个布尔值转换为一个数字会产生以下结果:

  • __abc0 -> __abc1
  • __abc0 -> __abc1

考虑三种可能的模式:

  • x大于y: (x > y) - (y < x) -> 1 - 0 -> 1
  • x等于y: (x > y) - (y < x) -> 0 - 0 -> 0
  • x小于y: (x > y) - (y < x) -> 0 - 1 -> -1

(替代)

  • x大于y: +(x > y) || -(x < y) -> 1 || 0 -> 1
  • x等于y: +(x > y) || -(x < y) -> 0 || 0 -> 0
  • x小于y: +(x > y) || -(x < y) -> 0 || -1 -> -1

这些逻辑等价于典型的排序比较器函数。

if (x == y) {
return 0;
}
return x > y ? 1 : -1;
<!doctype html>
<html>
<body>
<p id = "myString">zyxtspqnmdba</p>
<p id = "orderedString"></p>
<script>
var myString = document.getElementById("myString").innerHTML;
orderString(myString);
function orderString(str) {
var i = 0;
var myArray = str.split("");
while (i < str.length){
var j = i + 1;
while (j < str.length) {
if (myArray[j] < myArray[i]){
var temp = myArray[i];
myArray[i] = myArray[j];
myArray[j] = temp;
}
j++;
}
i++;
}
var newString = myArray.join("");
document.getElementById("orderedString").innerHTML = newString;
}
</script>
</body>
</html>

嵌套三元箭头函数

(a,b) => (a < b ? -1 : a > b ? 1 : 0)
var str = ['v','a','da','c','k','l']
var b = str.join('').split('').sort().reverse().join('')
console.log(b)

因为在javascript中可以直接比较字符串,所以这样做就可以了

list.sort(function (a, b) {
return a.attr > b.attr ? 1: -1;
})

排序函数中的减法仅在需要非字母(数字)排序时使用,当然它不适用于字符串

为什么问题中的方法不起作用的解释:

let products = [
{ name: "laptop", price: 800 },
{ name: "phone", price:200},
{ name: "tv", price: 1200}
];
products.sort( (a, b) => {
{let value= a.name - b.name; console.log(value); return value}
});


> 2 NaN

字符串之间的减法返回NaN。

附和@Alejadro的回答,正确的方法是——

products.sort( (a,b) => a.name > b.name ? 1 : -1 )

直接使用sort(),不需要任何-<

const areas = ['hill', 'beach', 'desert', 'mountain']
console.log(areas.sort())


// To print in descending way
console.log(areas.sort().reverse())

应该有升序和降序函数

if (order === 'asc') {
return a.localeCompare(b);
}
return b.localeCompare(a);

在javascript中,我们使用.localCompare方法对字符串进行排序

为打印稿

const data = ["jane", "mike", "salome", "ababus", "buisa", "dennis"];


const sort = (arr: string[], mode?: 'desc' | 'asc') =>
!mode || mode === "asc"
? arr.sort((a, b) => a.localeCompare(b))
: arr.sort((a, b) => b.localeCompare(a));




console.log(sort(data, 'desc'));// [ 'salome', 'mike', 'jane', 'dennis', 'buisa', 'ababus' ]
console.log(sort(data, 'asc')); // [ 'ababus', 'buisa', 'dennis', 'jane', 'mike', 'salome' ]




对JS

const data = ["jane", "mike", "salome", "ababus", "buisa", "dennis"];


/**
* @param {string[]} arr
* @param {"asc"|"desc"} mode
*/
const sort = (arr, mode = "asc") =>
!mode || mode === "asc"
? arr.sort((a, b) => a.localeCompare(b))
: arr.sort((a, b) => b.localeCompare(a));


console.log(sort(data, 'desc'));// [ 'salome', 'mike', 'jane', 'dennis', 'buisa', 'ababus' ]
console.log(sort(data, 'asc')); // [ 'ababus', 'buisa', 'dennis', 'jane', 'mike', 'salome' ]

如果你想控制语言环境(或大小写或重音),那么使用Intl.collator:

const collator = new Intl.Collator();
list.sort((a, b) => collator.compare(a.attr, b.attr));

你可以像这样构造collator:

new Intl.Collator("en");
new Intl.Collator("en", {sensitivity: "case"});
...

请参阅上面的doc链接。

注意:与其他解决方案不同,它以JS的方式处理null, undefined,即将它们移动到末尾。