Home > Blockchain >  JQ Loop over Bash array add elements
JQ Loop over Bash array add elements

Time:09-30

I do not seem to be able to find an answer, but have seen enough to know there is likely a better way of doing what I want to do.

Problem: I have a bash array. For each element in the bash array, I want to update a JSON array.

The JSON looks like the below. I am wanting to update the fruit array.

  "foods": {
    "perishable": {
      "fruit": []

I'll get an array of length n, for example:

fruit_array=("banana" "orange")

It should look something like this:

  "foods": {
    "perishable": {
      "fruit": [
        { 
          "001": {
            "002": "banana"
          }
        },
        { 
          "001": {
            "002": "orange"
          }
        }
      ]

Is there a nice way of doing this? At the moment I am trying the below:

#!/bin/bash

fruit_array=("banana" "orange")

for fruit in "${fruit_array[@]}"; do
   jq \
   --arg fruit $fruit \
   '.foods.perishables.fruit  = [{"001": {"002": $fruit}}]' \
   template.json > template_with_fruit.json
done

This doesn't work for the obvious reason that the template is being re-read, but I have messed around to get it consuming the output of the previous iteration and nothing comes out at the end. I am only able to update the template once.

However, I know this seems a little dodgy and suspect there is a cleaner, more jq way.

A previous - aborted - attempt went something like this:

jq \
--argjson fruit "$(printf '{"001": {"002": "%s"}}\n' \"${fruit_array[@]}\" | jq -nR '[inputs]')" \
'.foods.perishables.fruit  = $fruit' \

Which produced a escaped string which I couldn't do anything with, but at least hinted that there might be a neater solution to the standard bash loop.

I am missing something.

Any help would, as always, be appreciated.

CodePudding user response:

JQ can do all that on its own; you don't need a loop or anything.

jq '.foods.perishable.fruit  = (
  $ARGS.positional
  | map({"001": {"002": .}})
)' template.json --args "${fruit_array[@]}" >template_with_fruit.json

CodePudding user response:

If you pass your array as a space delimited string, you can use JQ like so:

jq --arg fruits "$fruit_array" \
    '.foods.perishable.fruit |= ($fruits | split(" ") | map({ "001": { "002": . } }))' input

{
  "foods": {
    "perishable": {
      "fruit": [
        {
          "001": {
            "002": "banana"
          }
        },
        {
          "001": {
            "002": "orange"
          }
        }
      ]
    }
  }
}

CodePudding user response:

Create whole json string at once with printf:

fruit_array=("banana" "orange")
printf -v fruits '{"001": {"002": %s}},' "${fruit_array[@]}"
$ echo $fruits
{"001": {"002": banana}},{"001": {"002": orange}},

Then add it to your template removing last comma:

$ echo ${fruits%,}
{"001": {"002": banana}},{"001": {"002": orange}}

"Numbers" could also be set like this:

fruit_array=("1 2 banana" "3 4 orange")
printf '{"%.3d": {"%.3d": %s}},' ${fruit_array[@]}
{"001": {"002": banana}},{"003": {"004": orange}},

CodePudding user response:

I suppose you could build the expression for what to add to the template, and then just run jq once on that.

printf -v fruits '[{"001": {"002": "%s"}}],' "${fruit_array[@]}"
fruits=${fruits%,}  # uglyness!
jq ".foods.perishables.fruit  = ${fruits//],[/,}" template.json > template_with_fruit.json
  • Related