Jordan
Jordan

Reputation: 1044

How can I compare two rethinkdb objects to create a new object that only contains their differences?

Say I have two objects stored in rethinkdb I wish to compare, let's call them old_val and new_val. As an example, let's say these values represent a TODO task that has changed owner and status:

{
  old_val: {
    status: 'active',
    content: 'buy apples',
    owner: 'jordan'
  },
  new_val: {
    status: 'done',
    content: 'buy apples',
    owner: 'matt'
  }
}

When I compare old_val and new_val, I'd like to yield a new object where new_val only contains the fields that differ from old_val. I want to do this in order to save bytes on the wire; and make rendering changes on my client easier. The result of the query should look something like this:

{
  old_val: {
    content: 'buy apples',
    owner: 'jordan',
    status: 'active'
  },
  new_val: {
    owner: 'matt',
    status: 'done'
  }
}

How would I do this?

Upvotes: 0

Views: 92

Answers (1)

Jordan
Jordan

Reputation: 1044

There are three separate parts to solving this problem:

  1. Generate a list of fields to compare
  2. Compare between common fields, include only fields which differ
  3. Create a new object

(1) A list of fields can be generated by using the keys() method. We can filter these fields to (2) only include those which exist in both old_val and new_val and whose values differ. We can then pass this sequence to concatMap() to build an array of key/value pairs like [key0, value0, key1, value1]. Finally, a new object can be constructed (3) from this sequence by applying it as arguments (using r.args()) to the r.object() function.

It comes together like this:

r.expr({
  old_val: {
    status: 'active',
    content: 'buy apples',
    owner: 'jordan'
  },
  new_val: {
    status: 'done',
    content: 'buy apples',
    owner: 'matt'
  }
}).do((diff_raw) =>
  r.expr({
    old_val: diff_raw('old_val'),
    // build an object only containing changes between old_val and new_val:
    new_val: r.object(r.args(
      diff_raw('new_val')
        .keys()
        // only include keys existing in old and new that have changed:
        .filter((k) =>
          r.and(
            diff_raw('old_val').hasFields(k),
            diff_raw('new_val')(k).ne(diff_raw('old_val')(k))
          )
        )
        // build a sequence of [ k0, v0, k1, v1, ... ]:
        .concatMap((k) => [k, diff_raw('new_val')(k)])
    ))
  })
)

This will return:

{
  "new_val": {
    "owner":  "matt" ,
    "status":  "done"
  } ,
  "old_val": {
    "content":  "buy apples" ,
    "owner":  "jordan" ,
    "status":  "active"
  }
}

Upvotes: 0

Related Questions