I'm still learning functional programming in JavaScript and I enjoy using Ramda a lot.
I have two arrays. I want to check if they have the same values, independent of order. I thought this could be done with equals
. But apparently
R.equals([1, 2], [2, 1]) // false
Is there an efficient way to check whether two arrays are equal? My arrays constist of objects and can hold up to X * 10E4 values if that matters with 1 < X < 10.
The reason it doesn't work like that -- beyond the fact that the Ramda function is named equals
and not isEqual
-- is that Arrays are intrinsically ordered containers. [1, 2]
is materially different from [2, 1]
.
The standard unordered container is the Set
. Unfortunately that is based on reference equality, so it could get multiple copies of items Ramda would think of as equal. So the most obvious answer will not work properly:
// ** Broken -- do not use **
const eqValues = (a1, a2) => R.equals(new Set(a1), new Set(a2))
console.log(eqValues(
[{x: 1}, {x: 2}],
[{x: 1}, {x: 3}]
)) //=> false
console.log(eqValues(
[{x: 1}, {x: 2}],
[{x: 2}, {x: 1}]
)) //=> true
because it would fail due to a length check in this case:
console.log(eqValues(
[{x: 1}, {x: 2}, {x: 2}],
[{x: 2}, {x: 1}]
)) //=> false, but should be true, since {x: 2} is the same as {x: 2}
Ramda does not expose its internal _Set
type -- and perhaps it should -- but it uses them in such functions as difference
, and through that in symmetricDifference
. These are appropriate functions for testing values whose value equality is in question.
So my answer would be similar to the one from bugs, but I would phrase it a bit differently:
const eqValues = compose(isEmpty, symmetricDifference)
console.log(eqValues(
[{x: 1}, {x: 2}],
[{x: 1}, {x: 3}]
)) //=> false
console.log(eqValues(
[{x: 1}, {x: 2}],
[{x: 2}, {x: 1}]
)) //=> true
console.log(eqValues(
[{x: 1}, {x: 2}],
[{x: 2}, {x: 1}, {x: 1}]
)) //=> true
<script src="https://bundle.run/[email protected]"></script><script>
const {compose, isEmpty, symmetricDifference} = ramda; </script>
However, if you need to test multiplicities -- that is, arr1
contains two copies of {x: 42}
and arr2
only has one, so they're different -- then I would use the answer from customcommander.
I would use eqBy
with countBy
:
You can use countBy
to build a "profile" of your array:
countBy(identity, [1, 2]);
//=> {"1": 1, "2": 1}
countBy(identity, [2, 1]);
//=> {"1": 1, "2": 1}
Then you can compare the two profiles with eqBy
:
eqBy(countBy(identity), [1,2], [2,1])
//=> true
There are many ways to achieve this with Ramda
One that comes to mind is
R.length([1,2]) === R.length([2,1]) && R.isEmpty(R.symmetricDifference([1,2], [2,1]))
edit: using R.difference
as opposed to R.symmetricDifference
wouldn't work, as the first one only returns the elements in the first list that are not contained in the second list.
R.difference([1,2], [2,1,3]) // -> []
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With