如何在JavaScript中对字符串进行排序

How to sort strings in JavaScript

我有一个要基于字符串类型的字段attr排序的对象列表。我试过用-

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

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


根据您的示例使用String.prototype.localeComparea:

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

我们强制a.attr为字符串以避免异常。从Internet Explorer 6和FireFox 1开始支持localeCompare。您还可以看到使用的不符合区域设置的以下代码:

1
2
3
4
5
if (item1.attr < item2.attr)
  return -1;
if ( item1.attr > item2.attr)
  return 1;
return 0;


更新答案(2014年10月)

我对这个字符串自然排序顺序非常恼火,所以我花了相当长的时间来研究这个问题。我希望这有帮助。好的。长话短说

localeCompare()字符支持很糟糕,只需使用它。正如Shog9所指出的,您的问题的答案是:好的。

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

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

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

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

通常,特殊字符(空格、短划线、和号、括号等)处理不正确。好的。

然后,您会发现它们在不同的地方混合在一起,通常可能是:好的。

  • 有些介于大写字母"Z"和小写字母"A"之间
  • 有些介于'9'和大写'A'之间
  • 有些在小写'Z'后面

当一个人希望特殊字符全部"分组"在一个地方,除了空格特殊字符可能(它总是第一个字符)。也就是说,要么全部在数字之前,要么全部在数字和字母之间(小写字母和大写字母一个接一个地"在一起"),要么全部在字母之后。好的。

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

定制实现研究:好的。

  • Natural Compare Litehttp:/ / / / github.com litejs自然比较Lite在持续失败的分类:http:////litejs github.com自然比较Lite /问题/ 1和http:/ / 1 / / bevututodavi jsbin.com编辑?JS控制台基本拉丁字符分类jsbin.com http:/ / / / 5 bevututodavi编辑?JS控制台
  • Natural Sorthttp:/ / / / github.com javve自然排序在排序:如问题持续,湖github.com javve http://////7自然排序问题的基本分类和海拉丁字符jsbin.com http:/ / / / 3 cipimosedoqe编辑?JS控制台
  • Javascript Natural Sorthttps:////JavaScript github.com重叠而被忽视的"自然排序:自2012年2月,在湖的持续失败的分类问题,http:////github.com嵌套JavaScript自然排序/问题/ 16
  • Alphanumhttp:/ / / / alphanum.js www.davekoelle.com文件排序,如在海上持续,http:/ / / 1 / jsbin.com tuminoxifuyo编辑?JS控制台

浏览器的"本土"自然字符串通过localeCompare()sort命令"的实现

最古老的localeCompare()执行(没有地点和选择的参数是支持IE6的+),http://。湖/美国/图书馆/ IU / s4esdbwz(V = vs.94).aspx(滚动到localecompare()方法)。这是建在一个localeCompare()法多在国际工作分类,甚至&;特殊字符。唯一的问题是,localeCompare()法使用"现场和使用是完全依赖sort命令的执行。换句话说,当使用localecompare如stringone.localecompare(stringtwo):Firefox,Safari,Chrome &;IE有一个字符串的排序顺序。

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

  • http:/ / / 1 / / beboroyifomu jsbin.com编辑?JS控制台基本拉丁字符和localecompare(比较)jsbin.com http:/ / / / / 2 viyucavudela基本拉丁字符的比较与测试localecompare()在IE8
  • jsbin.com http:/ / / / / 2 beboroyifomu编辑?JS控制台基本拉丁字符在字符串中的字符串比较的一致性检查:当一个字符是独立的VS
  • developer.mozilla.org https:///EN /文档/网络/美国/全球_ JavaScript字符串对象参考/ / / localecompare - ie11 +支持新的locale &;参数选择

"字符串排序的困难自然秩序"

A固体算法实施的意义:A,但也一致的覆盖范围是一个非常艰难的任务,人物)。包含超过2000 UTF8字符&;封面(120比脚本语言)。最后,有一些规范,这任务,它是所谓的"Unicode整理算法",可以发现在www.unicode.org http:/ / / / /报告tr10。你可以找到更多的信息关于这个问题我张贴在这个问题softwareengineering.stackexchange.com https://///257286是没有任何语言规范agnostic -一阶自然排序字符串

最后结论

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

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

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

进一步阅读:

  • https://softwarengineering.stackexchange.com/questions/257286/is-there-any-language-agnostic-specification-for-string-natural-sorting-order
  • 如何在javascript中进行字符串比较?
  • javascript:自然的字母数字字符串
  • 数字和字母元素的排序数组(自然排序)
  • 排序混合字母/数字数组
  • https://web.archive.org/web/20130929122019/http://my.opera.com/greyyvern/blog/show.dml/1671288
  • https://web.archive.org/web/20131005224909/http://www.davekoelle.com/alphanum.html
  • http://sniplr.com/view/36012/javascript-natural-sort/
  • http://blog.codingsurror.com/sorting-for-humans-natural-sort-order/

感谢Shog9的好回答,这让我走上了我相信的"正确"方向。好的。好啊。


答案(用现代的ecmascript)

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

1
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

所以这些逻辑相当于典型的排序比较器函数。

1
2
3
4
if (x == y) {
    return 0;
}
return x > y ? 1 : -1;


您应该在这里使用>或<和=。所以解决办法是:

1
2
3
4
5
6
7
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
2
3
4
5
6
7
8
9
10
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

1
2
3
4
5
6
7
8
9
10
11
12
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.

就这样。应用于字符串的Triple Equals运算符返回true iff参数完全相同的字符串(长度相同,相应位置的字符相同)。

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

但还有更多。在11.9.4以上,有一个简短的注释:

1
2
3
4
5
6
7
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.

嗯。现在怎么办?外部获得的字符串可以,而且很有可能是奇怪的单一编码,而我们温和的===将不会使它们公平。伊多克斯一〔1〕来营救:

1
2
3
4
5
6
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组件,因为它们是内部程序常量,那么===也可以工作。


嵌套三元箭头函数

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


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

它们的工作方式示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
+('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


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

1
item1.attr - item2.attr

因此,假设这些是数字(即item1.attr="1",item2.attr="2"),您仍然可以使用"=="运算符(或其他严格的计算器),前提是您确保类型。以下内容应该有效:

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

如果它们是字母数字,那么一定要使用localcompare()。


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


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<!doctype html>
<html>
<body>
<p id ="myString">zyxtspqnmdba
</p>
<p id ="orderedString">
</p>

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

</body>
</html>