在Javascript中比较两个列表的最佳方法是什么,就像在Python中一样?

时间:2013-02-13 11:56:48

标签: javascript python optimization coffeescript

我对JavaScript很新,但熟悉python。在Python中我得到了这个输出:

In [1]: [1,9,[5,4,2]] > [1,9,[14,5,4]]
Out[1]: False

在JavaScript中:

> [1,9,[5,4,2]] > [1,9,[14,5,4]]
true

似乎在比较之前将数组转换为字符串。

现在我想自己编写一个函数并遍历数组,比较每个元素。 我想出了这个coffeescript代码:

compare_list = (a, b)->
    if typeof a == "object" and typeof b != "object"
        return 1
    else if typeof a != "object" and typeof b == "object"
        return -1
    else if typeof a != "object" and typeof b != "object"
        if a > b
            return 1
        else if a < b
            return -1
        else
            return 0
    else if typeof a == "object" and typeof b == "object"
        for i in [0...a.length]
            if i > (b.length-1)
                return 1
            tmp = compare_list a[i], b[i]
            if tmp != 0
                return tmp
        if b.length > a.length
            return -1
        return 0

它以这种方式工作,但typeof a == "object"部分看起来不正确。是否有更简单/更好/更强大的解决方案?

感谢您的帮助。

5 个答案:

答案 0 :(得分:4)

这基本上是相同的算法,避免使用typeof运算符并在for循环中执行一些小技巧,以便每次都不检查数组的长度:

cmp = (a, b) -> (a > b) - (a < b)

cmpArray = (a, b)->
  aIsArray = Array.isArray a
  bIsArray = Array.isArray b

  return cmp a, b if not aIsArray and not bIsArray
  return -1       if not aIsArray and     bIsArray
  return 1        if     aIsArray and not bIsArray

  # Both are arrays.
  len = Math.min a.length, b.length
  for i in [0...len] by 1
    if tmp = cmpArray a[i], b[i]
      return tmp
  a.length - b.length

不幸的是,CoffeeScript没有提供任何类型的模式匹配。这将使这段代码更加干燥。如果您愿意,可以使用switch语句伪造一个穷人的模式匹配:

cmpArray = (a, b)->
  switch "#{Array.isArray a},#{Array.isArray b}"
    when 'false,false' then (a > b) - (a < b) # Compare primitives.
    when 'false,true' then -1
    when 'true,false' then 1
    else
      len = Math.min a.length, b.length
      for i in [0...len] by 1
        if tmp = cmpArray a[i], b[i]
          return tmp
      a.length - b.length

但这绝对不是非常惯用的CoffeeScript。如果CoffeeScript支持某种模式匹配,我肯定会选择这种类型的洗脱,因为我认为它只是一个单独的表达式,而不是(太多)早期返回。

答案 1 :(得分:1)

试图解决同样的问题,我也只提出了一个自定义解决方案。 https://gist.github.com/ruxkor/2772234

由于Javascript在比较对象时使用字符串强制,我认为有必要使用自定义比较函数来模拟Python行为。

答案 2 :(得分:1)

让我们简约:

function compare(a, b) {
    if (a instanceof Array && b instanceof Array) {
        for (var r, i=0, l=Math.min(a.length, b.length); i<l; i++)
            if (r = compare(a[i], b[i]))
                return r;
        return a.length - b.length;
    } else // use native comparison algorithm, including ToPrimitive conversion
        return (a > b) - (a < b);
}

(使用instanceof进行数组检测,请参阅this article进行讨论)

如果要使对象始终大于基元,可以将最后一行更改为

        return (typeof a==="object")-(typeof b==="object") || (a>b)-(a<b);

答案 3 :(得分:1)

我实现了一个JavaScript函数compareArrays,其行为类似于Python中的数组比较:

function compareArrays(a, b) {
    var aIsArray = Array.isArray(a),
        bIsArray = Array.isArray(b),
        cmp = 0;
    if (!aIsArray || !bIsArray) {
        throw new Error('Can\'t compare array to non-array: ' + a + ', ' + b);
    }

    _.find(a, function (aElem, index) {
        var bElem = b[index];
        if (Array.isArray(aElem) || Array.isArray(bElem)) {
            cmp = compareArrays(aElem, bElem);
        } else {
            cmp = (aElem > bElem) - (aElem < bElem);
        }

        if (cmp !== 0) {
            return true;
        }
    });

    return cmp;
}

它使用Underscore迭代数组,并递归处理嵌套数组。

请参阅我的fiddle,其中包含原始测试套件。

测试结果

[1,9,[5,4,2]] < [1,9,[14,5,4]]
[1,[1]] can't be compared to [1,1]
[1,[2]] > [1,[1]]
[2] > [1]
[1] == [1]
[] == []

答案 4 :(得分:0)

它非常丑陋,但这似乎可以完成这项工作:

var compareLists = function compare(listA, listB) {
    if (Array.isArray(listA)) {
        if (Array.isArray(listB)) {
            if (listA.length == 0) {
                if (listB.length == 0) {
                    return 0;
                } else {
                    return -1;
                }
            } else {
                if (listB.length == 0) {
                    return +1;
                } else {
                    return compare(listA[0], listB[0]) || 
                           compare(listA.slice(1), listB.slice(1));
                }
            }
        } else {
            return -1; // arbitrary decision: arrays are smaller than scalars
        }
    } else {
        if (Array.isArray(listB)) {
            return +1; // arbitrary decision: scalars are larger than arrays
        } else {
            return listA < listB ?  -1 : listA > listB ? + 1 : 0;
        }
    }
};

compareLists([1, 9, [5, 4, 2]], [1, 9, [14, 5, 4]]); // -1
compareLists([1, 9, [5, 4, 2]], [1, 9, [5, 4]]);     // +1
compareLists([1, 9, [5, 4, 2]], [1, 9, [5, 4, 2]]);  //  0

根据您的环境,您可能希望填充Array.isArray

if (!Array.isArray) {
    Array.isArray = function (obj) {
        return Object.prototype.toString.call(obj) === "[object Array]";
    };
}