I'd like to compare two arrays... ideally, efficiently. Nothing fancy, just true
if they are identical, and false
if not. Not surprisingly, the comparison operator doesn't seem to work.
var a1 = [1,2,3];
var a2 = [1,2,3];
console.log(a1==a2); // Returns false
console.log(JSON.stringify(a1)==JSON.stringify(a2)); // Returns true
JSON encoding each array does, but is there a faster or "better" way to simply compare arrays without having to iterate through each value?
The Practical Way
I think it's wrong to say a particular implementation is "The Right Way™" if it's only "right" ("correct") in contrast to a "wrong" solution. Tomáš's solution is a clear improvement over string-based array comparison, but that doesn't mean it's objectively "right". What is right anyway? Is it the fastest? Is it the most flexible? Is it the easiest to comprehend? Is it the quickest to debug? Does it use the least operations? Does it have any side effects? No one solution can have the best of all the things.
Tomáš's could say his solution is fast but I would also say it is needlessly complicated. It tries to be an all-in-one solution that works for all arrays, nested or not. In fact, it even accepts more than just arrays as an input and still attempts to give a "valid" answer.
Generics offer reusability
My answer will approach the problem differently. I'll start with a generic
arrayCompare
procedure that is only concerned with stepping through the arrays. From there, we'll build our other basic comparison functions likearrayEqual
andarrayDeepEqual
, etcIn my opinion, the best kind of code doesn't even need comments, and this is no exception. There's so little happening here that you can understand the behaviour of this procedure with almost no effort at all. Sure, some of the ES6 syntax might seem foreign to you now, but that's only because ES6 is relatively new.
As the type suggests,
arrayCompare
takes comparison function,f
, and two input arrays,xs
andys
. For the most part, all we do is callf (x) (y)
for each element in the input arrays. We return an earlyfalse
if the user-definedf
returnsfalse
– thanks to&&
's short-circuit evaluation. So yes, this means the comparator can stop iteration early and prevent looping through the rest of the input array when unnecessary.Strict comparison
Next, using our
arrayCompare
function, we can easily create other functions we might need. We'll start with the elementaryarrayEqual
…Simple as that.
arrayEqual
can be defined witharrayCompare
and a comparator function that comparesa
tob
using===
(for strict equality).Notice that we also define
equal
as it's own function. This highlights the role ofarrayCompare
as a higher-order function to utilize our first order comparator in the context of another data type (Array).Loose comparison
We could just as easily defined
arrayLooseEqual
using a==
instead. Now when comparing1
(Number) to'1'
(String), the result will betrue
…Deep comparison (recursive)
You've probably noticed that this is only shallow comparison tho. Surely Tomáš's solution is "The Right Way™" because it does implicit deep comparison, right ?
Well our
arrayCompare
procedure is versatile enough to use in a way that makes a deep equality test a breeze …Simple as that. We build a deep comparator using another higher-order function. This time we're wrapping
arrayCompare
using a custom comparator that will check ifa
andb
are arrays. If so, reapplyarrayDeepCompare
otherwise comparea
andb
to the user-specified comparator (f
). This allows us to keep the deep comparison behavior separate from how we actually compare the individual elements. Ie, like the example above shows, we can deep compare usingequal
,looseEqual
, or any other comparator we make.Because
arrayDeepCompare
is curried, we can partially apply it like we did in the previous examples tooTo me, this already a clear improvement over Tomáš's solution because I can explicitly choose a shallow or deep comparison for my arrays, as needed.
Object comparison (example)
Now what if you have an array of objects or something ? Maybe you want to consider those arrays as "equal" if each object has the same
id
value …Simple as that. Here I've used vanilla JS objects, but this type of comparator could work for any object type; even your custom objects. Tomáš's solution would need to be completely reworked to support this kind of equality test
Deep array with objects? Not a problem. We built highly versatile, generic functions, so they'll work in a wide variety of use cases.
Arbitrary comparison (example)
Or what if you wanted to do some other kind of kind of completely arbitrary comparison ? Maybe I want to know if each
x
is greater than eachy
…Less is More
You can see we're actually doing more with less code. There's nothing complicated about
arrayCompare
itself and each of the custom comparators we've made have a very simple implementation.With ease, we can define exactly how we wish for two arrays to be compared — shallow, deep, strict, loose, some object property, or some arbitrary computation, or any combination of these — all using one procedure,
arrayCompare
. Maybe even dream up aRegExp
comparator ! I know how kids love those regexps …Is it the fastest? Nope. But it probably doesn't need to be either. If speed is the only metric used to measure the quality of our code, a lot of really great code would get thrown away — That's why I'm calling this approach The Practical Way. Or maybe to be more fair, A Practical Way. This description is suitable for this answer because I'm not saying this answer is only practical in comparison to some other answer; it is objectively true. We've attained a high degree of practicality with very little code that's very easy to reason about. No other code can say we haven't earned this description.
Does that make it the "right" solution for you ? That's up for you to decide. And no one else can do that for you; only you know what your needs are. In almost all cases, I value straightforward, practical, and versatile code over clever and fast kind. What you value might differ, so pick what works for you.
Edit
My old answer was more focused on decomposing
arrayEqual
into tiny procedures. It's an interesting exercise, but not really the best (most practical) way to approach this problem. If you're interested, you can see this revision history.Here is a Typescript version:
Some test cases for mocha:
While this only works for scalar arrays (see note below), it is short:
Rr, in ECMAScript 6 / CoffeeScript / TypeScript with Arrow Functions:
(Note: 'scalar' here means values that can be compared directly using
===
. So: numbers, strings, objects by reference, functions by reference. See the MDN reference for more info about the comparison operators).UPDATE
From what I read from the comments, sorting the array and comparing may give accurate result:
Eg:
Then the above code would give
true
The reason is that identity or strict operator (===), it compares with no type conversion, that means if both values doesn’t have the same value and the same type, they won’t be considered equal.
take a look this link, it takes you out of doubt easy way to understand how identity operator works
I like to use the Underscore library for array/object heavy coding projects ... in Underscore and Lodash whether you're comparing arrays or objects it just looks like this: