Home > Net >  How can I use keys and values from jq to generate a bash associative array?
How can I use keys and values from jq to generate a bash associative array?

Time:02-21

From Json structure, I want a specific dictionary. From key's color yellow or red, I add id value.

[
  {
    "id": "9b058640",
    "type": "db",
    "color": "red",
    "host": "db1"
  },
  {
    "id": "0u858640",
    "type": "db",
    "color": "yellow",
    "host": "db2"
  },
  {
    "id": "0ui9k40",
    "type": "net",
    "color": "red",
    "host": "net1"
  },
  {
    "id": "5ty87a",
    "type": "net",
    "color": "yellow",
    "host": "net2"
  }
]

So I want to get the X dictionary

X=(
   ['yellow']="9b058640 5ty87a"
   ['red']="9b058640 0ui9k40"
 )

I could parse by value :

jq -c '.[] | select(.color | contains("red"))'

CodePudding user response:

You can use the @tsv operator in jq to emit tab-separated output, which a bash while read loop can easily parse as input.

Assuming your input JSON is in the variable s:

declare -A X=( )

while IFS=$'\t' read -r color id; do
  X[$color] ="$id "
done < <(jq -r '.[] | [.color, .id] | @tsv' <<<"$s")

The above does take a minor shortcut in that it leaves a trailing space after each item. If that's unacceptable for some reason, you can always go through the array and clean it up after the fact with a second loop:

for color in "${!X[@]}"; do
  X[$color]=${X[$color]%" "}
done

You can see this running in the sandbox at https://replit.com/@CharlesDuffy2/IndigoRemoteEngineering


Alternately, using eval:

#!/usr/bin/env bash
case $BASH_VERSION in ''|[0-3].*) echo "ERROR: bash 4.0  required" >&2; exit 1;; esac
declare -A X=( )
eval "$(
  jq -r '
    reduce .[] as $item ({}; .[$item.color]  = [$item.id])
    | to_entries[]
    | "X[\(.key | @sh)]=\(.value | join(" ") | @sh)"
  '
)" <file.json

CodePudding user response:

No need for ., source, eval or even looping in bash. All you need is declare and jq, which can construct the declaration using escaping with @sh and string interpolation:

declare -A X="($(
  jq -r '
    [ ("yellow", "red") as $color 
      | @sh "[\($color)]=\(map(select(.color == $color).id) | join(" "))"
    ] | join(" ")
  ' input.json
))"
$ echo "${X[yellow]}"
0u858640 5ty87a

$ echo "${X[red]}"
9b058640 0ui9k40

CodePudding user response:

Answer:

declare -A a
. <(jq -r '"a[\(.color)] =${a[\(.color)]  }\(.id)"' file.json)

Provides:

$ declare -p a
$ declare -A a=([red]="9b058640 0ui9k40" [yellow]="0u858640 5ty87a" )
# or
$ echo "${a[red]}"; echo "${a[yellow]}"
9b058640 0ui9k40
0u858640 5ty87a

Explanation:

  • Construct the following bash code in jq:
  • a[color] =${a[color] }id
  • ${a[color] } expands to , only if a[color] is empty.
  • "\(.id)" is jq string interpolation - its replaced with the value of .id
  • use . and a process sub to source this code
  • Related