【问题标题】:Three-way compare function for Arrays in JavascriptJavascript中数组的三向比较函数
【发布时间】:2014-05-27 05:44:12
【问题描述】:

还有其他关于How to compare arrays in JavaScript? 的问题。我想知道的是编写/使用像 Array.sort() 所需的三路比较函数的最直接方法。这是一个使用默认设置的示例,但效果不佳:

> [ [4,5,10], [4,5,6], [4,1,2] ].sort() // no compare function, uses the default one
[ [ 4, 1, 2 ],
  [ 4, 5, 10 ], // oops, string sorting makes 10 < 6
  [ 4, 5, 6 ] ]

这是我想出的:

// return -1 if lhs is "less" than rhs, +1 if "greater", and 0 if equal
// if lhs and rhs have different lengths, only the shorter part will be considered
function compareArrays(lhs, rhs) {
  for (var ii = 0; ii < lhs.length; ii++) {
    if (lhs[ii] < rhs[ii]) {
      return -1;
    } else if (lhs[ii] > rhs[ii]) {
      return 1;
    }
  }
  return 0;
}

这给了我们想要的东西:

> [ [4,5,10], [4,5,6], [4,1,2] ].sort(compareArrays)
[ [ 4, 1, 2 ],
  [ 4, 5, 6 ],
  [ 4, 5, 10 ] ]

是否有更像单线的东西,或者我必须在任何时候定义自己的函数?

不要求支持旧浏览器。使用 jQuery 或 Underscore 之类的库是可以的。

查看此问题的一种方式是“应用到每对元素的标准三向比较中的第一个非零值”。但即便如此,我也没有在现有的库中找到合适的选择。

【问题讨论】:

  • 您当前的解决方案不是“单线”吗?您可以使用forEach 和条件运算符?: 将排序函数减少到几乎一个行,但这只会在一个地方减少几个字符的代码量,而不是让它更快或更可靠。
  • 它不会比你的 compareArrays() 函数小很多。
  • "使用 jQuery 或 Underscore 之类的库是可以的。" - 那么,把你的函数变成一个库。有你的单线。
  • 顺便说一句,您的函数不适用于不同长度的数组。

标签: javascript arrays three-way-merge


【解决方案1】:

我会选择一个通用的比较生成器函数,以便以函数方式使用:

function compareArrays(compareItems) {
    return function(a, b) {
        for (var r, i=0, l=Math.min(a.length, b.length); i<l; i++)
            if (0 != (r = compareItems(a[i], b[i])))
                return r;
        return a.length - b.length;
     };
}
// Examples:
var compareNumberArrays = compareArray(function(a,b){ return a-b; }),
    compareGenericArrays = compareArray(function(a,b){ return +(a>b)||-(b>a); });

现在你可以使用

[ [4,5,10], [4,5,6], [4,1,2], [4,5] ].sort(compareNumberArrays)

是否有更像单线的东西,或者我必须在我想这样做时定义自己的函数?

比较数组对于单行来说太复杂了,您应该使用辅助函数。没有可以随处使用的内置功能。

【讨论】:

    【解决方案2】:

    可能不是最短的,但我能合理理解的最少的行是:

    function compareArrays(lhs, rhs) {
      var result;
      lhs.some(function(v, i) {
        return (result = v - rhs[i]);
      });
      return result;
    }
    

    或不太明智:

    function compareArrays(lhs, rhs, r) {
      lhs.some(function(v, i) {return (r = v - rhs[i])});
      return r;
    }
    

    编辑

    看来你不想要数字。比较部分可以是您想要的任何关系,例如对于字符串:

    function compareArrays(lhs, rhs, r) {
      lhs.some(function(v, i) {return (r = v < rhs[i]? -1 : v > rhs[i]? 1 : 0)});
      return r;
    }
    
    [['a','b','c'],['a','c','d'],['a','b','d']].sort(compareArrays) // a,b,c,a,b,d,a,c,d 
    

    但是比较函数需要知道它得到了什么,所以它不会将数字排序为字符串或字符串排序为数字(等等......)。

    【讨论】:

    • 谢谢——some() 的使用很有趣,符合我对问题的概念表述。现在,如果只有一个标准的“三向比较”函数与sort()....
    • 经过进一步考虑,我意识到我真正想要的 some() 更像是“返回回调产生的第一个真实值,如果没有找到,则返回指定的默认值。”我在 Underscore 和 Lo-dash 中寻找类似的东西,结果空手而归。
    • 此功能不适用于不同长度的数组。你认为你可以轻松适应吗?
    • @JohnZwinck——这不是我的回答吗? some 返回第一个真实结果,如果没有找到则默认为 0。
    • @Bergi——是的,提供了如何处理它们的规则,例如包括 i in rhs 测试或类似的测试,以及返回 false 时要返回的值。
    【解决方案3】:

    如果你知道它们总是三元组的数字(长度为 3 的数组),你可以这样做:

    function combineNum(array) {
        return (array[0] * 100) + (array[1] * 10) + array[2];
    }
    
    function compareArrays(lhs, rhs) {
        return combineNum(rhs) - combineNum(lhs);
    }
    

    【讨论】:

    • 这也假设数组中的值始终是数字,但对我来说并非总是如此。
    • @JohnZwinck - 是的,它确实假设了这一点。您所有的示例都是数字的,所以我认为这就是您要解决的问题。哦,那是我缩短它的机会。那么,您在这里的真正目标是什么?您有一个有效的代码示例。您的项目中只需要一个副本,然后您就可以参考它。你为什么要剪掉一两行?
    • 这似乎应该是可能的,而不需要围绕这样一个微不足道的功能。我想我已经习惯了比较数组之类的语言更加内置......但即使那样,这感觉就像 jQuery 或 Underscore 应该能够在没有这种乏味的情况下做的事情。此外,即使我的工作功能也不是最佳的:如果 lhs.length 与 rhs.length 非常不同,它会浪费时间......我可以添加更多代码来解决这个问题,但如果存在现有的功能,这基本上是浪费时间可以的。
    • 这也是假设它们是个位数(或至少相等数量级),而不是任何数字。
    • @Bergi - 是的,如果您想允许更大的数字,您可以根据需要分散加权因子。只是想在这里提供另一种概念性的想法,不必进行如此多的逐个单元格的比较。
    猜你喜欢
    • 1970-01-01
    • 2011-12-03
    • 2020-06-22
    • 2017-04-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多