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) \
   '(regions
     | [{(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": {}
  }
}
Answer from peak on Stack Overflow
Discussions

add object to dynamic key
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session. You switched accounts on another tab or window. Reload to refresh your session. ... I'm looking to build object using jq and add object key to dynamic ... More on github.com
🌐 github.com
2
January 30, 2018
json - How to use jq to create an object with an arbitrary key from a sub array? - Stack Overflow
Communities for your favorite technologies. Explore all Collectives · Stack Overflow for Teams is now called Stack Internal. Bring the best of human thought and AI automation together at your work More on stackoverflow.com
🌐 stackoverflow.com
text processing - jq create object with property name from variable - Unix & Linux Stack Exchange
An object key can be any string, including "", "[]", "null", etc. jo -- -s "$n=$n" will help for n=--help or n=1, not for [], null, @x, a=b... jo's interface is severely broken by design IMO. ... That is use the variable $n as the key, not the "$n" string. jq -n is like echo null | jq. More on unix.stackexchange.com
🌐 unix.stackexchange.com
October 5, 2024
Construct JSON with a variable key using jq - Stack Overflow
I am trying to use jq to construct a hash in which a key name comes from a variable. Something like this: jq --null-input --arg key foobar '{$key: "value"}' This doesn't work, however, and gives the More on stackoverflow.com
🌐 stackoverflow.com
🌐
Reddit
reddit.com › r/bash › is it possible to use dynamic key in jq
r/bash on Reddit: Is it possible to use dynamic key in jq
March 23, 2022 -

Is it possible to do something like this:

jq -r --argjson i 0 --arg key "name" '.projects[$i].$key' input-file

I've tried enclosing $key in both parenthesis and square brackets unsuccessfully.

🌐
GitHub
github.com › jqlang › jq › issues › 1591
add object to dynamic key · Issue #1591 · jqlang/jq
January 30, 2018 - #!/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 .
Author   gdbtek
🌐
GitHub
gist.github.com › joar › 776b7d176196592ed5d8
Add a field to an object with JQ · GitHub
cat template.json | jq --argjson json "`<snippet.json`" '. + {foo: $json}' Copy link · Copy Markdown · To add a key to a nested object, you can use .key +=: echo '{"hello": {"value": "world"}}' | jq '.hello += {other_key: "new_val"}' { "hello": { "value": "world", "other_key": "new_val" } } If you are only adding or modifying a single value, there is no need to merge JSON.
Find elsewhere
🌐
GitHub
github.com › jqlang › jq › issues › 2529
Construct literal objects with subsequences of key-value pairs · Issue #2529 · jqlang/jq
January 24, 2023 - Describe the bug I want a syntax to construct objects with subsequences of key-value pairs. To Reproduce Consider this construction with an array: $ jq -n '["foo", (["bar", "baz"] | .[] | (. + "X")), "quux"]' [ "foo", "barX", "bazX", "qu...
Author   phs
Top answer
1 of 2
1

Elements of a stream are processed independently. So we have to change the input.

We could group the stream elements into an array. For an input stream, this can be achieved using --slurp/-s.[1]

jq -s '
   ( .[0].Columns[0] | map_values( tostring ) ) as $map |
   (
      .[0],
      (
         .[1:][] |
         .Users[] |= with_entries(
            .key = $map[ .key ]
         )
      )
   )
'

Demo on jqplay

Alternatively, we could use --null-input/-n in conjunction with input and/or inputs to read the input.

jq -n '
   input |
   ( .Columns[0] | map_values( tostring ) ) as $map |
   (
      .,
      (
         inputs |
         .Users[] |= with_entries(
            .key = $map[ .key ]
         )
      )
   )
'

Demo on jqplay

Note that your desired output isn't valid JSON. Object keys must be strings. So the above produces a slightly different document than requested.

Note that I assumed that .Columns is always an array of one exactly one element. This is a nonsense assumption, but it's the only way the question makes sense.


  1. For a stream the code generates, you could place the stream generator in an array constructor ([]). reduce can also be used to collect from a stream. For example, map( ... ) can be written as [ .[] | ... ] and as reduce .[] as $_ ( []; . + [ $_ | ... ] ).
2 of 2
1

The following has the merit of simplicity, though it does not sort the keys. It assumes jq is invoked with the -n option and of course produces a stream of valid JSON objects:

input
| . as $Columns
| .Columns[0] as $dict
| input # Users
| .Users[] |= with_entries(.key |= ($dict[.]|tostring))
| $Columns, .

If having the keys sorted is important, then you could easily add suitable code to do that; alternatively, if you don't mind having the keys of all objects sorted, you could use the -S command-line option.

🌐
jq recipes
remysharp.com › drafts › jq-recipes
jq recipes
April 16, 2024 - npm i $(echo $(npm outdated --json | jq -r 'to_entries | .[] | "\(.key)@\(.value.latest)"')) Change the above from .latest to .wanted for a safe upgrade. ... Add new property to every object in a nested object, i.e.
🌐
GitHub
gist.github.com › olih › f7437fb6962fb3ee9fe95bda8d2c8fa4
jq Cheet Sheet · GitHub
With the help of some of the tricks shown here, I came up with a way to transform my JSON. The result is on JqPlay. ... jq 'to_entries | .[] | {"rs": .key, "chrom":.value.chrom, "pos":.value.pos, "a1": .value.alleles | keys.[0], "a2": .value.alleles | keys | .[1:] | join(",") } | [.chrom, .pos, .a1, .a2] | @tsv'
Top answer
1 of 1
2

The main problem seems to be in your json data because you have:

{'a': 'apple', 'b', 'bananna'}

As far as I know using single quotes is not valid (use double quotes): json format.
The another problem I see is that you have a comma , between 'b' and 'bannana' (I assume you didn't notice that and you typed , instead of :)

So Your json data should be like this:

{"a": "apple", "b": "bananna"}

What is the proper syntax to get a key?

I usually see that the syntax '.key' is more used. However I think you should use any which works. For example, I see in your case that you are using a variable $key to get specific value from a json key. So, you should use something like ".$key" or maybe ."$key", .$key (I'm not sure if these are recommended):

Solution 1

FRUIT=$(echo $ALIASES | jq ".$key" -r)
#or
FRUIT=$(echo $ALIASES | jq ."$key" -r)
#or
FRUIT=$(echo $ALIASES | jq .$key -r)

If you use or want to use single quotes then you will not be able to pass any variable value to jq command. According to man bash:

Enclosing characters in single quotes (') preserves the literal value of each character within the quotes. A single quote may not occur between single quotes, even when preceded by a backslash

Therefore If you want to use single quotes to a json value by specifying some key you should use:

FRUIT=$(echo $ALIASES | jq '.somekey' -r)
#e.g.
FRUIT=$(echo $ALIASES | jq '.a' -r)
echo $FRUIT
#Output:
apple

You can check these answers for a better understanding about single quotes and double quotes

Solution 2 (using jq --arg)
I'm not sure why the code above doesn't work for you (using the double quotes). However there is another possible solution to pass variables to jq, you can try:

val="a"
echo $ALIASES | jq --arg key "$val" '.[$key]' -r

As you are using python to get the json then you should use the method json.dumps(your_json), for example:

python3 -c 'import json;aliases = json.load(open("file"));print(json.dumps(aliases))'
🌐
Stack Overflow
stackoverflow.com › questions › 79629191 › jq-create-object-fails-when-key-is-missing
json - Jq create object fails when key is missing - Stack Overflow
An imo simpler solution is to build the objects dynamically with the specified key names and values, using from_entries:
Top answer
1 of 1
3
$ jq -n '.a.x = 3 | .b.y = 4'
{
  "a": {
    "x": 3
  },
  "b": {
    "y": 4
  }
}

The jq expression first sets the x key beneath a to 3, and the resulting object is passed on via the pipe to another similar assignment, setting the y key of b to 4 in the same object.

However, this uses the static paths .a.x and .b.y.

Would you want to work with data given from the shell, then you could do the following for your two values:

jq -n --argjson value 3 'setpath($ARGS.positional; $value)' --args a x |
jq    --argjson value 4 'setpath($ARGS.positional; $value)' --args b y

This uses setpath() to set the path given by the list at the end of the command line (a and x in the first instance) to the value given to the $value variable. The result is then passed on to a similar jq invocation that adds a second value at another path (note the lack of -n here, though!) If the value should be added as a string (not a number), then use --arg in place of --argjson when you give the value.

The result of this pipeline would be

{
  "a": {
    "x": 3
  },
  "b": {
    "y": 4
  }
}

Depending on what form you have the path on in the shell, you might find it useful to pass a fully constructed path array to jq:

jq -n --argjson path '["a","x"]' --argjson value 3 'setpath($path; $value)' |
jq    --argjson path '["b","y"]' --argjson value 4 'setpath($path; $value)'

If you have the paths in the form .a.x and .b.y, then you can split these up with split() to form path arrays for setpath():

jq -n --arg path .a.x --argjson value 3 'setpath($path|split(".")[1:]; $value)' |
jq    --arg path .b.y --argjson value 4 'setpath($path|split(".")[1:]; $value)'

The [1:] on the result of split() is needed only if your path strings start with a dot. Note that this is the first variation of this solution that doesn't allow dots in key names.


With your clarified input taken into consideration, you may use

jq -n --argjson path '[".a.x", ".b.y"]' --argjson value '[3,4]' '[ ([$path,$value]|transpose[]) as [$p,$v] | setpath($p|split(".")[1:];$v) ] | add'

This first creates a single $p ("path") and $v ("value") pair from each combined element of the two arrays $path and $value. The $p string is then split in a similar manner as before, and the value $v is assigned to it with setpath().

The only "magic" here is the initial [$path,$value] | transpose[], which generates the two arrays

[".a.x",3]
[".b.y",4]

... and the fact that the whole operation takes place within an array constructor, forming the array

[
  {
    "a": {
      "x": 3
    }
  },
  {
    "b": {
      "y": 4
    }
  }
]

... which add then combines into our final product:

{
  "a": {
    "x": 3
  },
  "b": {
    "y": 4
  }
}

Again, dots are not allowed in key strings, and we also assume that the input arrays' length will always be the same.

Top answer
1 of 2
2

You could tweak your attempt as follows:

jq -s 'map({ (.id) : . }) | add' <data

However, it would be more efficient to use inputs and reduce with the -n command-line option instead of -s.

Of course, using this approach runs the risk of collisions.

You might also want to add del(.id)

2 of 2
1

Ah! I've got it! Or I've got one solution - please post if there's a better way.

jq -s '[group_by(.id)[]| add | { (.id) : . } ]|add' <data

https://jqplay.org/s/BfAdRBZUMW

  1. group_by groups the inputs by their .id value and produces an array of arrays - the inner arrays are the values that match on id.

  2. for each group the inner arrays are passed to add which, because the things in the inner arrays are objects, merges them.

  3. That leaves a 2 item array. We feed that to an object constructor which plucks the id as the key and the whole item as the value. This still leaves an array of items.

  4. the outer [] (starts at start of pattern) says take all those and feed it to add (again), which merges the final objects created in (3).

It works, but there may be a cleaner way.

EDIT

This is uglier but produces the same result and is ~24% faster on a 9MB dataset.

jq -s 'reduce [.[]|{ (.id) : . }][] as $item ({}; . * $item )' <data

This uses reduce <list> as <$var> (<initiation>; <iteration>) starting with an empty object {} and using the merge operator * starting from the incoming item . to create the output. I'm surprised it's faster, but I understand that group_by does a sort, so I guess that's an additional time cost.

🌐
Jqlang
jqlang.github.io › jq › manual
jq 1.8 Manual
The given exit_code (defaulting to 5) will be jq's exit status. For example, "Error: something went wrong\n"|halt_error(1). Produces an object with a "file" key and a "line" key, with the filename and line number where $__loc__ occurs, as values.
🌐
RapidAPI
paw.cloud › docs › dynamic-values › jq-processor
Filter responses using jq JSON processor dynamic value | RapidAPI for Mac Documentation
Filter responses using jq JSON processor dynamic value (Documentation of RapidAPI for Mac, the most advanced HTTP client for Mac)
Top answer
1 of 2
10

Like this:

$ jq '.compilerOptions.skipLibCheck=true' file.json
{
  "compileOnSave": false,
  "compilerOptions": {
    "baseUrl": "./",
    "skipLibCheck": true
  }
}
2 of 2
4

The easiest way to add the key with its value has been covered in another answer. That answer adds the key to the end of the list of keys in the compilerOptions object. Normally, the ordering of the keys does not matter, and if you need things ordered in a particular way, you will use an array. However, I'm noticing that you (for whatever reason) expect the key to be added first, before the existing baseUrl key.

We can add the key in that position by, instead of adding the new key to the existing object, instead, add the existing object's keys to the end of the new key. So given the existing JSON document,

{
  "compileOnSave": false,
  "compilerOptions": {
    "baseUrl": "./",
    "jerry": "Was a race car driver"
  }
}

... we may want to use

jq --argjson skipLibCheck true '.compilerOptions = $ARGS.named + .compilerOptions' file

Given our example document above, this would generate

{
  "compileOnSave": false,
  "compilerOptions": {
    "skipLibCheck": true,
    "baseUrl": "./",
    "jerry": "Was a race car driver"
  }
}

The $ARGS.named thing is an object which contains the key-value pairs defined with --arg and/or --argjson on the command line. In the example above, this would be {"skipLibCheck":true}. Note that the $ARGS feature was introduced after release 1.5 of jq.

With the older 1.5 release of jq, you may use

jq --argjson skipLibCheck true '.compilerOptions = { skipLibCheck: $skipLibCheck } + .compilerOptions' file

Use --arg instead of --argjson if you want the value to be the string true rather than the special boolean value true.

The following gives an alternative way of adding the key at the end (to what's mentioned in the other answer), which follows the same pattern as the above command. Note that I'm also switching to using --arg here to insert true as a string, just to show how that looks.

jq --arg skipLibCheck true '.compilerOptions += $ARGS.named' file

... which would give you

{
  "compileOnSave": false,
  "compilerOptions": {
    "baseUrl": "./",
    "jerry": "Was a race car driver",
    "skipLibCheck": "true"
  }
}

With the older 1.5 release of jq, you may use

jq --arg skipLibCheck true '.compilerOptions += { skipLibCheck: $skipLibCheck }' file