【问题标题】:How can I reliably subsort arrays using DOM methods?如何使用 DOM 方法可靠地对数组进行子排序?
【发布时间】:2012-06-01 07:23:20
【问题描述】:

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

也就是说……

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

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

这是我用于 RGB 排序的 JS 的 sn-p:

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 的传统进行的。然而,我最初试用的四种浏览器中的两种浏览器的结果都不是我所期望的。

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

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

更新

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

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

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

【问题讨论】:

  • 你可以在 javascript en.wikipedia.org/wiki/Category:Stable_sorts 中实现一个稳定的排序。
  • 也许如果你能做一些事情,比如用一些额外的唯一标签(比如数字)来增加正在排序的元素,那将会有所帮助。然后你可以确保你总是偏向一个特定的方向。也就是说,如果您给每种颜色一个“索引”属性,并按初始排序增加,那么只要两个红色/绿色/蓝色值相等,您就会按第二个“索引”值排序。
  • 我不得不补充说,如果您真的是 那个 Eric A. Meyer :-),能够为您提供帮助是我的荣幸
  • Li0liQ: 你有什么特别稳定的推荐吗?我不想花一个小时来实现一个,然后发现它是有史以来效率最低的排序,或者其他什么。
  • 我会尝试一个 jsfiddle,看看我的建议是否会有所帮助。 (此外,我并不是故意要给人留下一个傻瓜的印象;我很乐意帮助每个人,不管是不是“互联网传奇”:-)

标签: javascript arrays dom sorting


【解决方案1】:

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

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

但是,对于这个用例,我不必担心在三个不同的调用中进行排序并确保它们稳定等等;相反,我会一口气完成所有排序。我们可以将 rgb 颜色指示器 (255, 192, 80) 视为实际上是某个奇怪基数的大数字:为了避免过多的数学运算,假设它以 1000 为基数(如果该短语没有意义,请忽略它;只要想一想这就是将整个 rgb 属性转换为一个包含所有属性的数字,有点像 CSS 在级联中计算优先级的方式)。所以这个数字实际上可以被认为是 255,192,080。如果你为每一行计算这个数字,然后按这个数字排序,一切都会解决,你只需要排序一次:所以你可以做一个而不是三个排序:@987654321 @ 一切都会好起来的。

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

【讨论】:

  • 不用乘;只需比较最重要的颜色,如果它们不同,你就完成了;否则类似地移动第二种和第三种颜色。
  • 当然,你可以这样做:sorter.sort(function(a,b) { if (a.red!=b.red) return a.red-b.red; if (a. green!=b.green) return a.green-b.green; if (a.blue!=b.blue) return a.blue-a.blue; }),但这并不能很好地进行 Schwartzian 变换,而且我认为它比乘法更乏味:)
  • 好点。另一种选择是形成一个 8 字符的十六进制字符串。
  • 同意这样可行(即:转换为以 256 为基数(而不是以 1000 为基数),然后以十六进制表示该 base256 数字),但这对于解释来说有点数学繁重,我想: -)
  • 我不得不下班去学校接我的儿子,否则我会在 Twitter 上早些提出这个建议,埃里克 ;-) 一次比较所有值的单一排序调用,要么作为(组合的 0 填充值)字符串或数字。
【解决方案2】:

我会以不同的方式解决这个问题。您似乎正试图通过从标记中提取数据来重建所有数据,这可能是一项危险的任务;一种更直接的方法是,以您的程序从一开始就可以理解的格式表示您想要呈现到页面的所有数据,然后在页面加载时简单地重新生成标记,然后在随后的每个排序中重新生成标记。

例如:

var colorsData = [
  {
    keyword: 'mediumspringgreen',
    decimalrgb: {
      r: 0,
      g: 250,
      b: 154
    },
    percentrgb: {
      r: 0,
      g: 98,
      b: 60.4
    },
    hsl: {
      h: 157,
      s: 100,
      l: 49
    }
    hex: '00FA9A',
    shorthex: undefined
  },
  {
    //next color...
  }
];

这样,您可以以任何您喜欢的方式对这个数组进行排序,而不是试图从标记中提取数据并将其拆分并重新分配等等。

但实际上,您似乎对排序功能很感兴趣。一个接一个地运行多个排序会得到意想不到的结果;如果发现前一个“列”相等,您必须运行一个比较下一个“列”的排序函数。 RGB 排序可能如下所示:

var decimalRgbForwards = function(a,b) {
  var a = a.decimalrgb,
      b = b.decimalrgb;
  if ( a.r === b.r ) {
    if ( a.g === b.g ) {
      return a.b - b.b;
    } else {
      return a.g - b.g;
    }
  } else {
    return a.r - b.r;
  }
};

因此,具有匹配 r 和 g 值的两种颜色将返回 b 值相等,这正是您要寻找的。​​p>

然后,您可以应用排序:

colorsData.sort(decimalRgbForwards);

..最后遍历该数组以重建表内的标记。

希望对你有帮助,先生-

【讨论】:

  • @hallvors 没说不是。请重新阅读我的第一段。
  • 显然,从服务器以 HTML 以外的方式发送数据的问题意味着您需要 JS 才能看到表格;渲染 HTML 然后用 JS 解析意味着没有 JS 的人不能排序,但是可以看到表格。
  • 一个有趣的方法,keekerdc——谢谢!不过,sil 表达了我的基本关切。我确实希望即使在没有 JS 的情况下也能显示该表格,我意识到我并没有在帖子中将其作为先决条件。对不起!我可能会将排序链接的创建转移到 JS,这样如果 JS 关闭,我就不会出现断开的链接。
  • @sil 和 Eric:可以肯定的是,这是一个值得关注的问题。作为回应,我建议将您的初始标记和数据的 JSON 表示与页面一起发送。否则,您实际上是在将 DOM 用作数据存储模型和视图。它可能适用于像这个这样的较小的“应用程序”,但为了代码的清晰和数据的完整性,将两者分开通常是一个好习惯,特别是如果您打算稍后构建一些更大的东西马路。我认为 Eric 的最后一个想法是好的:一点点渐进增强。 :)
猜你喜欢
  • 2012-04-16
  • 1970-01-01
  • 2020-01-10
  • 2012-01-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多