javascript - 如何使用 DOM 方法可靠地对数组进行子排序?

标签 javascript arrays dom sorting

预先注意:我在这里没有使用 jQuery 或其他库,因为我想了解我写的内容以及它为什么有效(或无效),所以请不要用库或库插件来回答这个问题。我并不反对库,但对于这个项目而言,它们不利于我的编程目标。

也就是说……

http://meyerweb.com/eric/css/colors/我使用自己编写的 DOM 函数添加了一些列排序。问题是,虽然它非常适合按字母顺序排列字符串的简单情况,但当我尝试对多个数字项进行排序时,浏览器之间的结果不一致——实际上,当我尝试对两个子排序进行排序时。

例如,如果您在 OS X 上的 Safari 或 Firefox 中单击“Decimal RGB”几次,您将获得我想要的结果。在 Chrome 或 Opera(还是 OS X)中做同样的事情,你会得到截然不同的结果。是的,Safari 和 Chrome 在这里有所不同。

这是我用于 RGB 排序的 JS 片段:

sorter.sort(function(a,b){
    return a.blue - b.blue;
});
sorter.sort(function(a,b){
    return a.green - b.green;
});
sorter.sort(function(a,b){
    return a.red - b.red;
});

(sorter 是我要排序的数组。)

排序是按照另一个 StackOverflow 问题“How does one sort a multi dimensional array by multiple columns in JavaScript?”的传统完成的” 和 its top answer .然而,在我最初尝试的四种浏览器中的两种中,结果并不是我所期望的。

我觉得(哈!)这与数组排序“不稳定”有关——这里没有争论!——但我不知道如何以一致、可靠的方式克服它。我真的需要一些帮助来理解问题和查看解决方案,或者至少是对解决方案的一般描述。

我意识到可能有 600 万种方法可以优化 JS 的其余部分(是的,我使用了全局)。我仍然是一个 JS 新手,并试图通过实践来纠正这个问题。现在,让我感到困惑的是数组排序,在继续清理其他地方的代码之前,我可以在这段脚本中使用一些帮助。提前致谢!

更新

除了下面很好的解释和建议之外,我还得到了一个更紧凑的解决方案:

function rgbSort(a,b) {
    return (a.red - b.red || a.green - b.green || a.blue - b.blue);
}

尽管我还不太了解它,但我想我已经开始掌握它的轮廓了,这就是我现在正在使用的。感谢大家的帮助!

最佳答案

好的。因此,正如您所发现的,您的问题是默认的 JavaScript 排序不能保证稳定。具体来说,我认为在您看来它是这样工作的:我将按蓝色排序,然后当我按绿色排序时,排序器只会上下移动我的数组中的条目,但保持它们按蓝色排序。可悲的是,宇宙的安排并不是那么方便;允许内置的 JS 排序按照它喜欢的方式进行排序。特别是,它允许只将数组的内容放入一个大桶中,然后按你要求的排序将它们拉出来,完全忽略它之前是如何排列的,看起来至少有些浏览器正是这样做的。

对于您的特定示例,有几种解决方法。首先,您仍然可以在三个单独的调用中进行排序,但要确保这些调用稳定地进行排序:这意味着在按蓝色排序后,您将稳定地按绿色排序,这将为您提供一个按绿色排序的数组,在其中的蓝色顺序(即,正是您正在寻找的)。我的可排序库通过实现“振动筛排序”或“鸡尾酒排序”方法 (http://en.wikipedia.org/wiki/Cocktail_sort) 来做到这一点;本质上,这种排序方式会遍历列表并上下移动项目。 (特别是,它做的只是将所有列表项放入桶中,然后按顺序将它们拉回来。)维基百科文章中有一个漂亮的小图。这意味着“子排序”保持排序——也就是说,排序是稳定的,这会给你想要的东西。

但是,对于这个用例,我不会担心在三个不同的调用中进行排序并确保它们稳定等等;相反,我会一次性完成所有排序。我们可以将 rgb 颜色指示器 (255, 192, 80) 视为实际上是某个奇怪底数中的一个大数字:为避免过多的数学运算,假设它以 1000 为底数(如果该短语没有意义,请忽略它;只是想想这就像将整个 rgb 属性转换为一个包含所有属性的数字,有点像 CSS 如何计算级联中的优先级)。所以这个数字实际上可以被认为是 255,192,080。如果你为你的每一行计算这个数字,然后按这个数字排序,一切都会成功,你只需要做一次排序:所以你可以做一个而不是做三种排序:sorter.sort(function(a,b) { return (a.red*1000000 + a.green*1000 + a.blue) - (b.red*1000000 + b.green*1000 + b.blue) } 和一切都会好起来的。

从技术上讲,这有点低效,因为每次调用排序函数时都必须计算“以 1000 为基数”,每行可能(很可能)不止一次。如果这是一个大问题(您可以通过对其进行基准测试来解决),那么您可以使用 Schwartzian 变换(抱歉这里的所有流行语):基本上,您可以计算出以 1000 为基数的数字对于每一行一次,将它们全部放在一个列表中,对列表进行排序,然后遍历排序后的列表。因此,创建一个看起来像 [ [255192080, <table row 1>], [255255255, <table row 2>], [192000000, <table row 3>] ] 的列表,对该列表进行排序(使用类似 mylist.sort(function(a,b) { return a[0]-b[0]; }) 的函数),然后遍历该列表并将每个 appendChild 到表中,这将按顺序对整个表进行排序。对于您已有的表格,您可能不需要最后一段,但它可能很有用,而且了解这个技巧肯定不会有什么坏处,sorttable.js 也使用它。

关于javascript - 如何使用 DOM 方法可靠地对数组进行子排序?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10759018/

相关文章:

java - 查找数组中小于 M 下一个元素的元素

javascript - 检查 2 个输入字段和一个选择字段,以便在全部填写完毕后进行更改

Javascript 返回错误的一天

javascript - Angular JS 数据表 - 字母数字排序

javascript - 如何根据第一次 HTTP 调用获取的 ID 呈现数据

Javascript console.log 返回 c() 而不是对象的值

ios - 数组追加会覆盖 Realm 对象的最后一个索引

php $_POST 数组并更新 MySQL

JavaScript - 无法在 Jest 测试中获取 jsdom 文档

javascript - 如何在nodejs中的ejs文件中显示警报