DEV Community

Ken Fukuyama
Ken Fukuyama

Posted on • Edited on • Originally published at blog.kenev.net

Merge 2 Objects in Rego

This is the final post of "Object Merging in Rego" series.

If you are not comfortable with Rego yet, I'd recommend you read the previous posts before continuing.

When the keys collide with the merging objects, let's say the latter object's value wins. With that in mind, in order to merge objects, we need to:

  1. Gather the keys of the 2 objects
  2. Pick the values of the objects and if the keys collide, pick the latter object's value

In order to implement this, "Set Comprehensions" and "Object Comprehensions" come in handy.

Gather keys of 2 objects

Suppose we have 2 objects like the following:

object1
{
  "a": "value1",
  "b": "value2"
}
object2
{
  "b": "value3",
  "c": "value4"
}

If we're merging these 2 objects, what we first want is the keys a, b, c. How can we gather these? With Set comprehensions.

This is how it will look like in Rego:

collect_keys = ks {
  ks := {k | some k; _ = input.a[k]} | {k | some k; _ = input.b[k]}
}

Maybe it looks a bit difficult for beginners but you'll get used to it sooner or later.

Let's look at one part of it:

{k | some k; _ = input.a[k]}

This is like saying

  • Iterate over input.a's keys
  • Put it inside the local variable k
  • Collect it

So if input.a is { "a": "value1", "b": "value2" }, the result will be { "a", "b" }. In addition, when the input.b is { "b": "value3", "c": "value4" }, the result will be { "b", "c" } and the rule above will look like this:

collect_keys = ks {
  ks := { "a", "b" } | { "b", "c" }
}

Which evaluates to ks == { "a", "b", "c" } because it is evaluating an OR with 2 sets.

You can check this out in the following playground:

https://play.openpolicyagent.org/p/bw0aM0bRHM

Pick values from 2 objects

Now that we have the keys of the final merged object, we need to gather the values of the 2 objects. What we'd want to do is:

  1. Iterate over the keys we gathered above
  2. Pick the value corresponding with the key. Be sure to prioritize the latter object's value over the former one.

This is where the pick_first function comes in handy from the 2nd post of this series.

Assuming that pick_first has already been declared, the following Rego function shows how 2 objects get merged.

merge_objects(a, b) = c {
    ks := {k | some k; _ = a[k]} | {k | some k; _ = b[k]}
    c := {k: v | some k; ks[k]; v := pick_first(k, b, a)}
}

Note that the object b comes first and object a next in the pick_first function which means that the values of object b are prioritized.

Wrap up

The final version of merging 2 objects in Rego looks like this (which you can find here in the official docs):

has_key(x, k) { _ = x[k] }

pick_first(k, a, b) = a[k]
pick_first(k, a, b) = b[k] { not has_key(a, k) }

merge_objects(a, b) = c {
    ks := {k | some k; _ = a[k]} | {k | some k; _ = b[k]}
    c := {k: v | some k; ks[k]; v := pick_first(k, b, a)}
}

merged = o {
  o := merge_objects(input.x, input.y)
}

If the input is as follows:

{
  "x": {
    "a": true,
    "b": false
  },
  "y": {
    "b": "foo",
    "c": 4
  }
}

The merged output becomes like this:

{
  "merged": {
    "a": true,
    "b": "foo",
    "c": 4
  }
}

Note that object y's key/value "b": "foo" is chosen because it is the latter object when merging.

For those who want to play around, here is the playground code:

https://play.openpolicyagent.org/p/LzJDiUQQFY

Reference

Top comments (0)