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
5 Answers
Reset to default 4This 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]";
};
}