8

In this answer there is a simple function that will return array equality for arrays that contain primitive values.

However, I'm not sure why it works. Here is the function:

function arrays_equal(a,b) { return !!a && !!b && !(a<b || b<a); }

I'm mostly interested in the second half; this bit:

!(a<b || b<a)

Why does the < and > work when comparing the arrays but the == doesn't?

How do the less than and greater than methods work within JavaScript?

Community
  • 1
  • 1
Bill Rawlinson
  • 600
  • 1
  • 4
  • 22

3 Answers3

11

With </>, the arrays are converted to strings first, and as such do not provide a reliable method of checking equality.

== does not work because objects are checked by reference:

[] == []; // false, two separate objects

var a = [];
a == a; // true, refer to the same object

The </> trick is flawed:

var a = [1, [2, 3]],
    b = [[1, 2], 3];

!(a<b || b<a); // true

This evaluates to true, because they are both converted to the string "1,2,3" before they are checked (</> do not "directly" work for objects).

So basically, you are comparing equality of the strings. For strings, a == b is indeed the same as !(a<b || b<a) - </> for strings check character codes, so two equal strings are neither "smaller" nor "greater" because that's not the case for any character code in the strings.

pimvdb
  • 151,816
  • 78
  • 307
  • 352
  • @Sarfraz: Yes, until you discover things like this yourself after some nasty debugging... – pimvdb Feb 17 '12 at 19:40
  • Have recently started learning it **seriously**, going forward :). You should have a blog by the way :) – Sarfraz Feb 17 '12 at 19:41
  • 1
    A more reliable alternative might be `JSON.stringify(a) == JSON.stringify(b)`. – gilly3 Feb 17 '12 at 19:45
  • 1
    @gilly: True, unless the arrays [contain objects themselves](http://stackoverflow.com/questions/9191791/javascript-compare-arrays-of-objects-optimal-way/9191823#comment11566854_9191823). – pimvdb Feb 17 '12 at 19:47
  • 1
    @gilly3: There'll be other issues as well. `JSON.stringify([function(){}]) == JSON.stringify([,]) // true`. –  Feb 17 '12 at 19:50
  • @pimvdb so, does it (1,2,3) means "49, 50, 51" or 49, 50, 51 or anything else ? – The Alpha Feb 17 '12 at 19:51
  • @Heera: I'm sorry, what are those numbers (`49`) referring to? – pimvdb Feb 17 '12 at 19:52
  • As you said for strings check character codes and is that mean (1=49, 2=50, 3=51), i.e [1,2,3]>[1,2,3] means 49, 50, 51 > 49, 50, 51 ? I meant, is this the way the javascript interpreter sees these, please, I'm confused. – The Alpha Feb 17 '12 at 20:00
  • 1
    @Heera: Ah I missed that - I don't know those codes off the top of my head :) Yes, you're correct, but note that the commas are also in the string. Anyway, consider a more trivial example: `"1" > "2"` is false because `49 > 50` is false. Likewise, the opposite is true. – pimvdb Feb 17 '12 at 20:01
  • Oh! Thank you very so much, it was a grate help, my image on my mind is now so clear. Thanks again! – The Alpha Feb 17 '12 at 20:06
  • thank you, each of the distinct answers so far have each taught me something; I appreciate you taking the time to answer! – Bill Rawlinson Feb 17 '12 at 20:25
  • 2
    The point of most of these arguments is basically that you can't use something simple like this as a generic utility function. But, from a more practical standpoint, if your arrays are in a known format (eg, comparing two simple string arrays), a function like this is certainly adequate. – gilly3 Feb 17 '12 at 20:36
  • @gilly3: Most certainly; I just think it's a good idea to know of its "quirks" if you are using someone else's function. – pimvdb Feb 17 '12 at 20:39
  • As this answer(http://stackoverflow.com/a/3198202/913845) suggests you should just use Underscore.js isEqual: http://documentcloud.github.com/underscore/#isEqual Underscore.js has tons of other useful stuff so it is well worth including in your page if you are doing any serious JavaScript on it. – Zar Shardan Feb 07 '13 at 01:40
5

However, I'm not sure why it works.

It doesn't work. Consider

arrays_equal(["1,2"], [1,2])

produces true even though by any definition of array equality based on element-wise comparison, they are different.

arrays_equal([[]], [])

and

arrays_equal([""], [])

are also spurious positives.

Simply adding length checking won't help as demonstrated by

arrays_equal(["1,2",3], [1,"2,3"])

arrays_equal(
    ["",","],
    [",",""])

EDIT:

If you want a succinct way to test structural similarity, I suggest:

function structurallyEquivalent(a, b) {
  return JSON.stringify(a) === JSON.stringify(b);
}

It doesn't stop early on inputs that are obviously different -- it walks both object graphs regardless of how disimilar they are, but so does the function in the OP.

One caveat: when you're using non-native JSON.stringify, it may do strange things for cyclic inputs like:

var input = [];
input[0] = input;
Mike Samuel
  • 118,113
  • 30
  • 216
  • 245
4

You can compare any two objects using ==. But since > and < are not defined for objects, they are converted to strings. Therefore, [1,2,3]>[2,1,3] is actually doing "1,2,3">"2,1,3"

Diego
  • 18,035
  • 5
  • 62
  • 66
  • 1
    this is not correct. Conversion is not always to string. [11.8.5](http://es5.github.com/#x11.8.5) specifies that object conversion happens with type hint `"number"` so `arrays_equal(new Date(1), 1)` is `true`. – Mike Samuel Feb 17 '12 at 20:13
  • Thanks; and to Mike for the clarification in his comment! – Bill Rawlinson Feb 17 '12 at 20:26