-
-
Save th507/5158907 to your computer and use it in GitHub Desktop.
/* | |
* @name arrayEqual | |
* @description check if two arrays are equal | |
* @param {arr} Array to check | |
* @param {arr} Array to check | |
* @return {Boolean} Boolean, returns true if arrays are the same | |
*/ | |
function arrayEqual(a, b) { | |
var i = Math.max(a.length, b.length, 1); | |
while(i-- >= 0 && a[i] === b[i]); | |
return (i === -2); | |
} | |
/* | |
* @name flattenArray | |
* @description flattens multi-dimension array into one-dimension array | |
* useful for manipulating function arguments like flattenArray(arguments) | |
* @usage flattenArray(arr) | |
* eg. | |
* [1, 2] => [1, 2] | |
* [1, [[[[[[2]]]]]]] => [1, 2] | |
* [1,[2,3],[[[[4]]],5]] => [1, 2, 3, 4, 5] | |
* | |
* @param {arr} Array to flatten | |
* @return {Array} Array, one-dimension array | |
*/ | |
function flattenArray(arr) { | |
var r = []; | |
while (!arrayEqual(r, arr)) { | |
r = arr; | |
arr = [].concat.apply([], arr); | |
} | |
return arr; | |
} |
The arrayEqual() approach is certainly unusual, but it's 100% legitimate (it actually fails in two cases, see below).
The i
variable is declared as Math.max(a.length, b.length, 1)
, therefore it will be 1 when the function is given two empty arrays, else it'll be the length of the longest array among a
and b
.
This means that the while
loop will always:
- check for its condition at least twice
- run its loop at least once.
Since the cycle breaks as soon as the two arrays are different, the case where the two arrays are of different length is accounted, except when either a
or b
contains the undefined
value, in which case the condition check may fail:
arrayEqual([1, 2, undefined], [1, 2]) === true // even though a.length == 3 and b.length == 2
Another case where this check fails is when comparing the NaN
value:
arrayEqual([NaN], [NaN]) === false
but this is due to the sicked nature of NaN
of not being equal to itself rather than this specific algorithm.
Discarded these two special cases, which are probably not an issue for the use of the function in flatten
, we may consider the limit case, i.e. when both arrays are of length 1. In fact, if i == 1
, the while
loop will become something like this:
condition A:
is i less than 0 ? # the first time this is false, since i is 1
either way, decrement i by 1. # now i is 0
if condition A was true, then: # and it was at least once
condition B:
is a[0] === b[0] ?
if a[0] is different than b[0], the loop will break here, and i will be 0, thus the function returns false;
else:
-- here there would be the while loop, but it's empty --
back to the condition check:
condition A:
is i less than 0 ? # nope, it is 0
either way, decrement i # now i is -1
condition B:
is a[-1] === b[-1] ? # yes, they're both undefined!
-- here there would be the while loop, but it's empty --
back to
condition A:
is i less than 0? # yes, it is now -1
either way, decrement i # now i is -2!
BREAK
control reaches here if and only if all the array elements were equal, and here i is -2
The function arrayEqual
may be improved to take the 1st special case into account by extending it a bit:
function arrayEqual(a, b) {
if (a.length !== b.length) return false;
// ...
}
as for the second case, I don't see an elegant way to do it; maybe using isNaN
.
As regards the [].concat.apply([], arr)
, it's a pretty standard way to "1-level flatten" an array; [].concat
is the same as Array.prototype.concat, while concat.apply just applies concat
using as this
argument to itself the first argument passed to it (in this case, []
, which is used as the base array for the concatenation).
This works great, but I do not understand the tricks you are applying here at all. Would you be so kind as to explain your cleverness?
For example, why the obfuscated approach to comparing the two arrays in arrayEqual? I think you're trying to walk down the array elements from last to first comparing each one, but I don't understand the approach nor how you would ever reach a value of i === -2. JSLint (not that I 100% agree with its suggestions) throws up all over this line. And, I thought I understood .apply but I cannot grasp how you're applying it here. Anyway, thank you in advance for taking the time to respond.