I have the following code which reads all the fields
of a Json file (the path being PRIVATE_REGISTRATION_FILE
and stores them into an associative array (PRIVATE_FIELDS
) which I query later in my code:
declare -A PRIVATE_FIELDS
for PRICING_FIELD in $(jq -c -r '.fields[]' "${PRIVATE_REGISTRATION_FILE}")
do
FIELD_KEY=$(jq -r '.label' <<< "${PRICING_FIELD}")
PRIVATE_FIELDS["${FIELD_KEY}"]=${PRICING_FIELD}
done
The problem is that I do this several times with several files, even though the logic is always the same.
Hence, I was thinking to extract this logic into a function but I'm having a hard time passing the map parameter to it.
This is what I attempted:
function update_array
{
FILE_NAME=$1
eval "declare -A MAP="${2#*=}
for PRICING_FIELD in $(jq -c -r '.fields[]' "${FILE_NAME}")
do
FIELD_KEY=$(jq -r '.label' <<< "${PRICING_FIELD}")
MAP["${FIELD_KEY}"]=${PRICING_FIELD}
done
}
Which I call like this:
declare -A PRIVATE_FIELDS
update_array "myFile.json" "$(declare -p PRIVATE_FIELDS)"
However it doesn't work, the map remains empty.
echo ${PRIVATE_FIELDS["someKey"]}
>>> (empty)
I have tried literally each solution proposed in this answer but none of them worked. What am I doing wrong?
Bash version: 4.2.46(2)-release
Additional note, the Json file looks like this (apparently the calls to jq
may be reduced):
{
"name": "Something",
"fields": [
{
"label": "key1",
"value": "value1",
"other": "other1"
},
{
"label": "key2",
"value": "value2",
"other": "other2"
}
]
}
When you use declare
in a function, you're actually making the variable local. See help declare
at a bash prompt.
Use a nameref (requires bash version 4.3+):
function update_array
{
local FILE_NAME=$1
local -n MAP=$2 # MAP is now a _reference_ to the caller's variable
# the rest stays the same
for PRICING_FIELD in $(jq -c -r '.fields[]' "${FILE_NAME}")
do
FIELD_KEY=$(jq -r '.label' <<< "${PRICING_FIELD}")
MAP["${FIELD_KEY}"]=${PRICING_FIELD}
done
}
then you simply pass the array name
declare -A PRIVATE_FIELDS
update_array "myFile.json" PRIVATE_FIELDS
declare -p PRIVATE_FIELDS
To more efficiently iterate over the JSON file:
$ jq -c -r '.fields[] | "\(.label)\t\(.)"' file.json
key1 {"label":"key1","value":"value1","other":"other1"}
key2 {"label":"key2","value":"value2","other":"other2"}
That's assuming the labels don't contain any tab characters.
Using that, plus your older bash version, you can do this
Assuming that the result arrays will be in the global scope
update_array() {
local filename=$1 varname=$2
local -A map
while IFS=$'\t' read -r label json; do
map[$label]=$json
done < <(
jq -c -r '.fields[] | "\(.label)\t\(.)"' "$filename"
)
eval declare -gA "$varname=$(declare -p map | cut -d= -f2-)"
}
You'd call it like
$ echo $BASH_VERSION
4.2.45(1)-release
$ update_array tmp/file.json myArray
$ declare -p myArray
declare -A myArray='([key2]="{\"label\":\"key2\",\"value\":\"value2\",\"other\":\"other2\"}" [key1]="{\"label\":\"key1\",\"value\":\"value1\",\"other\":\"other1\"}" )'
$ for label in "${!myArray[@]}"; do
> printf '"%s" => >>%s<<\n' "$label" "${myArray[$label]}"
> done
"key2" => >>{"label":"key2","value":"value2","other":"other2"}<<
"key1" => >>{"label":"key1","value":"value1","other":"other1"}<<