【问题标题】:Will these two array sorting algorithms ever produce different output for any inputs?这两种数组排序算法会为任何输入产生不同的输出吗?
【发布时间】:2013-11-15 02:09:27
【问题描述】:

其中数据是如下格式的元数组,

[
  [
        "qux doo",
        "adsf",
        "abcd",
        "zzzz",
        "898jwe9"
  ],
  [
        "abcd",
        "xxrwu",
        "urnr",
        "pupupu",
        "sdsdsd"
  ]
]

以下两种算法是否会针对不同的输入数据值产生不同的结果?

data.sort(function(a,b){
  return (JSON.stringify(a) < JSON.stringify(b)) - (JSON.stringify(a) > JSON.stringify(b));
});

data.sort(function(a, b) {
    for (var i = 0; i < Math.min(a.length, b.length); i++) {
        if (a[i] < b[i]) return -1;
        if (a[i] > b[i]) return 1;
    } 
    return (a.length > b.length) - (a.length < b.length); 
});

【问题讨论】:

  • 为什么你会使用第一个?只是好奇为什么这个问题很重要。
  • 因为它是通用的并且适用于非数组成员。
  • 同意@jfriend00。第二个会更快

标签: javascript arrays algorithm sorting


【解决方案1】:

JSON.stringify() 会转义某些字符(如双引号字符、反斜杠字符或任何控制字符),因此这些字符可能无法正确排序。

此外,由于空格的 ascii 代码低于双引号,因此如果您的两个数组以 "abcd ""abcd" 开头,它们将无法在 JSON 中正确排序。 "abcd " 应该在 "abcd" 之后,但是空格的 ascii 值比双引号低,所以它会排在前面。值末尾的感叹号也是如此。

如果根据您的 cmets 您还希望它适用于数字等非数组成员,则字符串比较不适用于比较具有不同位数的两个数字,因为 1000 不小于 2,但"1000" 小于"2"

另外,我建议您在第二个算法中使用.localeCompare() 比较两个字符串,因为它已经具有内置的正、负或零结果。


如果你所有的值都是字符串或者它们通过.toString()正确排序,你可以像这样使用.localeCompare()

data.sort(function(a, b) {
    var comp, i;
    for (i = 0; i < Math.min(a.length, b.length); i++) {
        if ((comp = a[i].localeCompare(b[i])) !== 0) return comp;
    } 
    return (a.length > b.length) - (a.length < b.length); 
});

.localeCompare 还有一些选项可用于区分大小写、忽略标点符号、如何处理重音字符以及其他一些选项。


根据您的评论和per MDN,与 Collat​​or 对象(仅在某些浏览器中可用)相比,您可以获得更好的性能。根据文档(我自己只尝试过一次此代码),它的工作原理如下:

var collater = new Intl.Collator();
data.sort(function(a, b) {
    var comp, i;
    for (i = 0; i < Math.min(a.length, b.length); i++) {
        if ((comp = collater.compare(a[i], b[i])) !== 0) return comp;
    } 
    return (a.length > b.length) - (a.length < b.length); 
});

大概必须有一些初始化或开销可以通过这种方式完成一次。也许他们会构建直接查找排序表。

但浏览器对 Collat​​or 对象的支持很少(IE 11、Chrome、没有 Firefox、没有 Safari),所以除非您在浏览器插件中使用它,因此代码仅适用于一个浏览器,否则您将拥有分支是否支持它并有两个实现。


附:如果您有任何数量可观的外部数组元素,从而多次调用排序回调,它将执行得非常糟糕。你至少可以让它每次只调用两次JSON.stringify(),而不是四次。

【讨论】:

  • 你能告诉我你将如何整合.localeCompare()吗?
  • @user2958725 - .localeCompare() 选项已添加到答案中。
  • 哦,我只是指让data 是一个平面数组,比如字符串,而不是数组的元数组,没有数字值作为最窄的比较原子。当您说“如果您的所有值都是字符串”时,您确实是指每个内部数组的所有值,对吗?你对 Intl.Collat​​or 了解吗?根据MDN doc for .localeCompare()
  • "当比较大量字符串时,比如对大数组进行排序,最好创建一个 Intl.Collat​​or 对象,并使用它的 compare 属性提供的函数。"你对那个对象有什么了解吗?我确实在不太好的硬件上对 100K+ 值的非常大的数组进行排序。我查阅了文档,但对我来说似乎真的很困惑。您发布的内容非常完美,所以如果您有时间,您介意向我展示使用 Intl.Collater 的等效内容吗?我要感谢您提供的超级有用且内容丰富的建议,非常感谢!
  • 我以前从未听说过 Collat​​er。我只是点击了MDN doc 的链接,并按照他们显示的方式实现了它,我添加到了我的答案中。问题是很多浏览器都不支持它(没有 Firefox,没有 Safari,只有 IE 11)。它似乎在 Chrome 中有效:jsfiddle.net/jfriend00/Ymc9h
猜你喜欢
  • 2020-02-09
  • 2022-12-05
  • 2013-10-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-10
  • 1970-01-01
相关资源
最近更新 更多