最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

What is the best way to compare two lists in Javascript as you would do in Python? - Stack Overflow

programmeradmin0浏览0评论

I'm quite new with JavaScript but familiar with python. In Python I get this output:

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

In JavaScript:

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

It seems that the arrays are converted into a string before the parison.

Now i wanted to write a function by myself and walk through the array an pare each element. I came up with this coffeescript code:

pare_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 = pare_list a[i], b[i]
            if tmp != 0
                return tmp
        if b.length > a.length
            return -1
        return 0

It works this way but the typeof a == "object" part looks not correct to me. Is there a simpler/better/more robust solution?

Thanks for your help.

I'm quite new with JavaScript but familiar with python. In Python I get this output:

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

In JavaScript:

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

It seems that the arrays are converted into a string before the parison.

Now i wanted to write a function by myself and walk through the array an pare each element. I came up with this coffeescript code:

pare_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 = pare_list a[i], b[i]
            if tmp != 0
                return tmp
        if b.length > a.length
            return -1
        return 0

It works this way but the typeof a == "object" part looks not correct to me. Is there a simpler/better/more robust solution?

Thanks for your help.

Share Improve this question asked Feb 13, 2013 at 11:56 HWM-RockerHWM-Rocker 6172 gold badges9 silver badges17 bronze badges 3
  • Solution for what? To check whether a and b are arrays? – Bergi Commented Feb 13, 2013 at 12:19
  • Yes, or if there is a simpler solition. – HWM-Rocker Commented Feb 13, 2013 at 12:23
  • 2 Then you should not check for objects, but use Array.isArray – Bergi Commented Feb 13, 2013 at 12:25
Add a ment  | 

5 Answers 5

Reset to default 4

This is basically the same algorithm avoiding the typeof operator and doing a little trick in the for loop to not check for the lengths of the arrays every time:

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

Unfortunately, CoffeeScript does not provide any sort of pattern matching. That would make this code much more DRY-er. You can fake a poor man's pattern matching using a switch statement if you want:

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

But this is definitely not very idiomatic CoffeeScript. If CoffeeScript supported some kind of pattern matching, i'd definitely go for this kind of slution, as i think it reads very nicely being just a single expression and not relying (too much) on early returns.

Trying to solve the same issue, I too only came up with a custom solution. https://gist.github./ruxkor/2772234

As Javascript is using string coercion when paring object, I think it is necessary to use a custom parison function to simulate Python behavior.

I had a go at implementing a JavaScript function pareArrays that behaves like array parison in Python:

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

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

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

    return cmp;
}

It uses Underscore to iterate over arrays, and recurses to handle nested arrays.

See my fiddle which includes a primitive test suite.

Test Results

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

Let's make it minimalistic:

function pare(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 = pare(a[i], b[i]))
                return r;
        return a.length - b.length;
    } else // use native parison algorithm, including ToPrimitive conversion
        return (a > b) - (a < b);
}

(using instanceof for Array detection, see this article for discussion)

If you want to make objects always greater than primitives, you can change the last line to

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

It's pretty ugly, but this seems to do the job:

var pareLists = function pare(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 pare(listA[0], listB[0]) || 
                           pare(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;
        }
    }
};

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

Depending upon your environment, you might want to shim Array.isArray:

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

评论列表(0)

  1. 暂无评论