如何在JavaScript中对字符串排序


343

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

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

但发现-在JavaScript 中似乎不适用于字符串。如何基于带有字符串类型的属性对对象列表进行排序?


1
看到JavaScript case insensitive string comparisonstackoverflow.com/questions/2140627/...
阿德里安要

对于快速的“国际化”解决方案(我想只是部分,因为这可能无法涵盖世界上所有的重音符号),您可能只想忽略重音符号,即删除它们。那么只有做你的字符串比较,看Javascript : remove accents/diacritics in stringsstackoverflow.com/questions/990904/...
阿德里安要

2
有趣的是,杰夫·阿特伍德(Jeff Atwood)自己在2007年就此常见问题写了一篇博客文章,请参阅blog.codinghorror.com/sorting-for-humans-natural-sort-order
Adrien Be

Answers:


619

使用String.prototype.localeCompare每个示例:

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

我们将a.attr强制为字符串以避免出现异常。自Internet Explorer 6和Firefox 1 localeCompare开始受支持。您可能还会看到以下使用的不符合语言环境的代码:

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

81
在有人犯与我一样的草率错误之前,它是本地e比较,而不是localCompare。
ento 2012年

12
第一种解决方案将“ A”放在“ z”之后但在“ Z”之前,因为它正在对字符ASCII值进行比较。localeCompare()不会遇到此问题,但不会理解数字,因此您可以像大多数语言中的排序比较一样获得[“ 1”,“ 10”,“ 2”]。如果您想对UI前端进行排序,请查看alphanum /自然排序算法stackoverflow.com/questions/4340227/…stackoverflow.com/questions/4321829/…–
Dead.Rabit

2
需要注意的是localeCompare()只支持现代浏览器:在写作的时候IE11 +,看到developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
阿德里安要

3
不,我的意思是表的第一行@Adrien-IE支持localeCompare()回溯许多版本,但不支持指定 11版之前的语言环境。还要注意Dead.Rabit链接到的问题。
Shog14年

3
@ Shog9我不好,看来自IE6以来就受支持!请参阅msdn.microsoft.com/zh-cn/library/ie/s4esdbwz(v=vs.94).aspx上的(向下滚动/搜索到localeCompare()方法)。但要注意的一件事是,在旧的实现中,我们不使用locales和options参数(在IE11之前使用的一种),使用的语言环境和排序顺序完全取决于实现,也就是说:Firefox,Safari,Chrome和IE不要以相同顺序对字符串排序。参见code.google.com/p/v8/issues/detail?id=459
Adrien Be

165

更新的答案(2014年10月)

我真的很讨厌这种字符串自然排序顺序,所以我花了很多时间来研究这个问题。我希望这有帮助。

长话短说

localeCompare()角色支持很糟糕,只需使用它即可。如所指出Shog9,您的问题的答案是:

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

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

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

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

通常,特殊字符(空格,破折号,“&”号,方括号等)未正确处理。

然后,您会发现它们在不同的位置混合出现,通常可能是:

  • 有些会在大写字母“ Z”和小写字母“ a”之间
  • 有些会在'9'和大写字母'A'之间
  • 有些将在小写字母“ z”之后

当一个人希望所有特殊字符都被“分组”在一个地方时,除了空格特殊字符(始终是第一个字符)。也就是说,要么全部在数字之前,要么全部在数字和字母之间(小写和大写字母“一个”接另一个地),或者全部在字母之后。

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

有关自定义实现的研究:

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

localeCompare()IE6 +支持最早的实现(没有语言环境和选项参数),请参见http://msdn.microsoft.com/zh-cn/library/ie/s4esdbwz(v = vs.94).aspx(向下滚动到localeCompare( ) 方法)。内置localeCompare()方法在排序(甚至国际字符和特殊字符)方面做得更好。使用该localeCompare()方法的唯一问题是“使用的语言环境和排序顺序完全取决于实现”。换句话说,当使用诸如stringOne.localeCompare(stringTwo)之类的localeCompare时:Firefox,Safari,Chrome和IE对字符串的排序顺序不同。

对浏览器本地实现的研究:

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

实施可靠的算法(意味着:一致但又涵盖了广泛的字符)是一项艰巨的任务。UTF8包含2000多个字符涵盖120多个脚本(语言)。最后,有一些针对此任务的规范,称为“ Unicode排序算法”,可以在http://www.unicode.org/reports/tr10/上找到。您可以在我发布的这个问题上找到有关此问题的更多信息/software/257286/is-there-any-language-agnostic-specification-for-string-natural-sorting-order

定论

因此,考虑到我遇到的javascript自定义实现所提供的当前支持水平,我们可能永远不会看到有什么东西能够接近支持所有这些字符和脚本(语言)的。因此,我宁愿使用浏览器的本地localeCompare()方法。是的,它确实存在跨浏览器不一致的缺点,但基本测试表明,它涵盖了更大范围的字符,允许可靠且有意义的排序顺序。

因此,正如所指出Shog9,您的问题的答案是:

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

进一步阅读:

多亏Shog9的好回答,我相信我朝着“正确”的方向前进


38

答案(现代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))

描述

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

  • true -> 1
  • false -> 0

考虑三种可能的模式:

  • 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;

1
正如我对使用此技巧的较早答案进行评论时,仅代码的答案可以通过解释其工作方式而变得更加有用。
丹·达斯卡斯库

添加的描述
mpyw

您能否评论这比localeCompare好还是坏?
Ran Lottem

3
@RanLottem localeCompare和标准比较得出不同的结果。您期望哪个? ["A", "b", "C", "d"].sort((a, b) => a.localeCompare(b))以不区分大小写的字母顺序排序,而["A", "b", "C", "d"].sort((a, b) => (a > b) - (a < b))以代码点顺序排序
mpyw

我知道,这似乎是主要的症结所在。关于性能差异有什么想法吗?
Ran Lottem

13

您应在此处使用>或<和==。因此解决方案将是:

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;
});

1
附带说明,这将无法处理字符串与数字的比较。例如:'Z'<9(false),'Z'> 9(false),'Z'== 9(false!)。JavaScript中的傻NaN ...
加藤2014年

7

嵌套三元箭头功能

(a,b) => (a < b ? -1 : a > b ? 1 : 0)

7

由于可以在javascript中直接比较字符串,因此可以完成工作

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

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


6

我已经为此烦恼了很长时间,所以我终于对此进行了研究,并为您提供了这条漫长的原因来说明事情的现状。

规格

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.

嗯 现在怎么办?外部获得的字符串可能而且很可能是奇怪的单一代码,而我们的绅士===不会公平对待它们。在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; dr;

要比较javascript中的字符串,请使用localeCompare; 如果您知道字符串没有非ASCII成分,因为它们是例如内部程序常量,那么它===也可以使用。


0

在初始问题中的操作中,您正在执行以下操作:

item1.attr - item2.attr

因此,假设这些是数字(即item1.attr =“ 1”,item2.attr =“ 2”),只要确保输入类型,您仍然可以使用“ ===”运算符(或其他严格的求值器)。以下应该工作:

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

如果它们是alphaNumeric,则请使用localCompare()。


0
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

3
通过解释纯代码答案的工作方式,可以使它们更加有用。
Dan Dascalescu

-2
<!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>

1
请添加一些有关这将如何解决问题的信息。不欢迎仅使用代码的答案。谢谢。
wayneOS '18

在这里,您要对字符串中的字符进行排序,这不是要求的内容。您可以使用“ Array.sort”简单地实现这种排序,例如str.split(“”)。sort().
join

-2
var str = ['v','a','da','c','k','l']
var b = str.join('').split('').sort().reverse().join('')
console.log(b)

尽管这段代码可以解决问题,但包括解释如何以及为什么解决该问题的说明,确实可以帮助提高您的帖子质量,并可能导致更多的投票。请记住,您将来会为读者回答问题,而不仅仅是现在问的人。请编辑您的答案以添加说明,并指出适用的限制和假设。
戴夫
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.