Merge 2 Objects in Rego
Tuesday, October 15, 2019
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:
- Gather the keys of the 2 objects
- 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 keysPut 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:
- Iterate over the keys we gathered above
- 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