8
4

2 回答 2

5

好的。因此,正如您所发现的,您的问题是不能保证默认的 JavaScript 排序是stable。具体来说,我认为在您的脑海中它是这样工作的:我将按蓝色排序,然后当我按绿色排序时,排序器只会上下移动我的数组中的条目,但保持它们按蓝色排序。可悲的是,宇宙的排列并不那么方便。内置的 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]; })),然后遍历该列表并将每个 s 附加到表中,这将按顺序对整个表进行排序。您可能不需要为您所拥有的表格提供最后一段,但它可能很有用,而且知道这个技巧肯定不会有什么坏处,sorttable.js 也使用该技巧。

于 2012-05-25T18:17:56.370 回答
1

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

例如:

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 值相等,这正是您要寻找的。

然后,您可以应用排序:

colorsData.sort(decimalRgbForwards);

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

希望对你有帮助,先生-

于 2012-05-25T18:21:38.910 回答