2

I'm looking to build object using jq and add object key to dynamic key and I could not figure out. Here is my sample script:

#!/bin/bash -e

environments=('development' 'stage' 'production')
regions=('us-east-1' 'us-west-2')

tree='{}'

for environment in "${environments[@]}"
do
    echo "${environment}"
    # Or do something else with environment

    tree="$(jq --arg jqEnvironment "${environment}" '. | .[$jqEnvironment] = {}' <<< "${tree}")"

    for region in "${regions[@]}"
    do
        echo "${region}"
        # Or do something with region

        tree="$(jq --arg jqEnvironment "${environment}" --arg jqRegion "${region}" '. | .[$jqEnvironment] | .[$jqRegion] = {}' <<< "${tree}")"
    done
done

jq . <<< "${tree}"

actual outputs

{
  "us-west-2": {}
}

But what I want is this

{
  "development": {
    "us-east-1": {},
    "us-west-2": {}
  },
  "stage": {
    "us-east-1": {},
    "us-west-2": {}
  },
  "production": {
    "us-east-1": {},
    "us-west-2": {}
  }
}

I could not figure out, please help!

1 Answer 1

4

The following script produces the desired result, and should be fairly robust:

#!/bin/bash

environments=('development' 'stage' 'production')
regions=('us-east-1' 'us-west-2')

jq -n --slurpfile e <(for e in "${environments[@]}" ; do echo "\"$e\""; done) \
   --slurpfile r <(for r in "${regions[@]}" ; do echo "\"$r\"" ; done) \
   '($r | map ({(.): {}}) | add) as $regions
     | [{($e[]): $regions}] | add'

The main point to note here is that in order to construct an object with a dynamically determined key, one has to use parentheses, as in {(KEY): VALUE}

Of course, if the values for the "environments" and "regions" were available in a more convenient form, the above could be simplified.

Output

{
  "development": {
    "us-east-1": {},
    "us-west-2": {}
  },
  "stage": {
    "us-east-1": {},
    "us-west-2": {}
  },
  "production": {
    "us-east-1": {},
    "us-west-2": {}
  }
}
Sign up to request clarification or add additional context in comments.

8 Comments

Thanks @peak, I ran the script and it does not look like it output valid json format though { "development": { "us-east-1": {}, "us-west-2": {} } } { "stage": { "us-east-1": {}, "us-west-2": {} } } { "production": { "us-east-1": {}, "us-west-2": {} } }
is it possible to keep 2 for-loop separated because I need to do something with environment and region variables? Your short form solution is pretty cool but not flexible for me to use environment or region variables for other actions. Thanks a lot for your solution.
--argfile variable-name filename: Do not use. Use --slurpfile instead. (This option is like --slurpfile, but when the file has just one text, then that is used, else an array of texts is used as in --slurpfile.)
1. I tweaked the script to produce a single JSON object. 2. As you say, --argfile has been deprecated but it is perfectly fine here, and has the (possible) advantage of working in versions of jq that don't have --slurpfile.
look like you updated and it caused errors now "jq: error: syntax error, unexpected '[', expecting '|' (Unix shell quoting issues?) at <top-level>, line 2: [{($e[]): $regions}] | add jq: 1 compile error"
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.