6

There is an equals function in Ramdajs which is totally awesome, it will provide the following:

// (1) true
R.equals({ id: 3}, { id: 3})

// (2) true
R.equals({ id: 3, name: 'freddy'}, { id: 3, name: 'freddy'})

// (3) false
R.equals({ id: 3, name: 'freddy'}, { id: 3, name: 'freddy', additional: 'item'});

How would I go about enhancing this function, or in some other way produce a true result for number 3

I would like to ignore all the properties of the rValue not present in the lValue, but faithfully compare the rest. I would prefer the recursive nature of equals remain intact - if that's possible.

I made a simple fiddle that shows the results above.

Sandun Chathuranga
  • 2,242
  • 2
  • 13
  • 27
Jim
  • 14,952
  • 15
  • 80
  • 167
  • Does this should return `true`: `eq({foo: {bar: 'test'}}, {foo: {bar: 'test', additional: 'item'})`. So does it behaves the same for inner objects recursively? – iofjuupasli May 04 '16 at 08:19
  • yes, it's completely recursive - which is why it's *awesome* – Jim May 04 '16 at 09:58

4 Answers4

7

There's a constraint on equals in order to play nicely with the Fantasy Land spec that requires the symmetry of equals(a, b) === equals(b, a) to hold, so to satisfy your case we'll need to get the objects into some equivalent shape for comparison.

We can achieve this by creating a new version of the second object that has had all properties removed that don't exist in the first object.

const intersectObj = (a, b) => pick(keys(a), b)

// or if you prefer the point-free edition
const intersectObj_ = useWith(pick, [keys, identity])

const a = { id: 3, name: 'freddy' },
      b = { id: 3, name: 'freddy', additional: 'item'}

intersectObj(a, b) // {"id": 3, "name": "freddy"}

Using this, we can now compare both objects according to the properties that exist in the first object a.

const partialEq = (a, b) => equals(a, intersectObj(a, b))

// again, if you prefer it point-free
const partialEq_ = converge(equals, [identity, intersectObj])

partialEq({ id: 3, person: { name: 'freddy' } },
          { id: 3, person: { name: 'freddy' }, additional: 'item'})
//=> true

partialEq({ id: 3, person: { name: 'freddy' } },
          { id: 3, person: { age: 15 }, additional: 'item'})
//=> false
Scott Christopher
  • 6,458
  • 23
  • 26
3

Use whereEq

From the docs: "Takes a spec object and a test object; returns true if the test satisfies the spec, false otherwise."

whereEq({ id: 3, name: 'freddy' }, { id: 3, name: 'freddy', additional: 'item' })

The other way around is to develop your own version. It boils down to:

if (is object):
  check all keys - recursive
otherwise:
  compare using `equals`

This is recursive point-free version that handles deep objects, arrays and non-object values.

const { equals, identity, ifElse, is, mapObjIndexed, useWith, where } = R

const partialEquals = ifElse(
  is(Object),
  useWith(where, [
    mapObjIndexed(x => partialEquals(x)),
    identity,
  ]),
  equals,
)

console.log(partialEquals({ id: 3 }, { id: 3 }))
console.log(partialEquals({ id: 3, name: 'freddy' }, { id: 3, name: 'freddy' }))
console.log(partialEquals({ id: 3, name: 'freddy' }, { id: 3, name: 'freddy', additional: 'item' }))
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>
Tymek
  • 3,000
  • 1
  • 25
  • 46
  • 1
    There's an edge case: I wonder what would this do with template containing circular structure :) It's probably fine, but I didn't test it yet. – Tymek Dec 06 '18 at 15:37
0

I haven't used Ramda.js before so if there's something wrong in my answer please be free to point out.

I learned the source code of Ramda.js

In src/equals.js, is where the function you use is defined.

var _curry2 = require('./internal/_curry2');
var _equals = require('./internal/_equals');

module.exports = _curry2(function equals(a, b) {
  return _equals(a, b, [], []);
});

So it simply put the function equals (internally, called _equals) into the "curry".

So let's check out the internal _equals function, it did check the length in the line 84~86:

if (keysA.length !== keys(b).length) {
  return false;
}

Just comment these lines it will be true as you wish.


You can 1) just comment these 3 lines in the distributed version of Ramda, or 2) you can add your own partialEquals function to it then re-build and create your version of Ramda (which is more recommended, from my point of view). If you need any help about that, don't hesitate to discuss with me. :)
iplus26
  • 2,518
  • 15
  • 26
  • flip! I looked at the code too! I assumed that it would take a lot more effort than just changing those few lines, but as it turns out, it works perfectly. It doesn't ignore array's with elements between matches, but I think that's actually correct. All the scenarios I have tested seem to work perfectly. So much so, I created a pull request for the changes. https://github.com/ramda/ramda/pull/1758 – Jim May 04 '16 at 08:23
-1

This can also be accomplished by whereEq

R.findIndex(R.whereEq({id:3}))([{id:9}{id:8}{id:3}{id:7}])
Jim
  • 14,952
  • 15
  • 80
  • 167