jsonnet

jsonnet : provide function to merge multiple objects into a single object


With jsonnet, I have multiple objects, and want to merge those into a single object. I know, that I can use the '+' operator to merge objects. But I do not know how to apply this operator to a list of objects.

Specifically, I want to provide a function, which merges an arbitrary number of objects. The objects can be provided as list items, or as dictionary fields.

My usecase is to provide application configuration, merged from a hierarchy of sources.

My current approach is:

Example: This is my initial config object, with N nested objects:

local config = {
  conf1: { key1: 'val', foo: 'bar' },
  confN: { keyN: 'val', foo: 'baz' }
}

I want to merge the nested N objects into a single object, result should be:

local merged_config = {
  key1: 'val',
  keyN: 'val',
  foo: 'baz'
}

I tried to transform the initial object into a list of objects, and use a comprehension to merge the objects:

# get array of nested objects
local values = std.objectValuesAll(config),

# init merged config
local merged_config = {}

# use comprehension to merge objects
{
  merged_config: merged_config + x
  for x in config
} 

But here I am stuck. I try to use a comprehensions as for-loop, but cannot get this to work. Probably this is the wrong approach.

Is there a better apporach then using a comprehension here? Any help is really appreciated!


Solution

  • The problem with comprehensions under this context is that you can't duplicate a key, i.e. below code would only work if there are no dups (foo in your example):

    {
      [k]: mainVal[k]
      for mainVal in std.objectValues(config)
      for k in std.objectFields(mainVal)
    }
    

    BTW besides the possible solution I provide here below, keep in mind that you're relying on the sort order of the "main" object keys (fields), which is not guaranteed when visiting objects' fields (that's why there's a std.sort() below)

    The solution uses std.foldl() over the array of config fields (conf1 ... confN in your case), to aggregate their values (and ok to override them).

    source

    local config = {
      conf1: { key1: 'val', foo: 'bar' },
      confN: { keyN: 'val', foo: 'baz' },
    };
    
    // Loop over config's keys, aggregating their values
    std.foldl(
      function(agg, key) agg + config[key],
      std.sort(std.objectFields(config)),
      {},
    )
    

    output

    $ jsonnet foo.jsonnet
    {
       "foo": "baz",
       "key1": "val",
       "keyN": "val"
    }