Iterate through dictionaries jq - shell

14,523

The problem with your code is that an array initialization in bash looks like this:

declare -a arr=(item1 item2 item3)

Items are separated by space or newline. You can also use:

declare -a arr(
    item1
    item2
    item3
)

However, the jq output in the example contains both spaces and newlines, that's why the reported behaviour is as expected.


Workaround:

I would get the keys first, pipe them to a read loop and then call jq for each item of the list:

jq -r '.images|keys[]' Contents.json | while read key ; do
    echo "image --$(jq ".images[$key]" Contents.json)"
done

You can also use this jq command if you don't care about pretty printing:

jq -r '.images[]|"image --" + tostring' Contents.json

To access a certain property of the subarray you can use:

jq -r '.images|keys[]' Contents.json | while read key ; do
    echo "image --$(jq ".images[$key].filename" Contents.json)"
done

The above node will print the filename property for each node for example.

However this can be expressed much simpler using jq only:

jq -r '.images[]|"image --" + .filename' Contents.json

Or even simpler:

jq '"image --\(.images[].filename)"' Contents.json
Share:
14,523
Johnykutty
Author by

Johnykutty

iOS developer (Swift/ObjectiveC) Opensource fan

Updated on June 24, 2022

Comments

  • Johnykutty
    Johnykutty almost 2 years

    I have a JSON like this

    {
      "images" : [
        {
          "size" : "29x29",
          "idiom" : "iphone",
          "filename" : "[email protected]",
          "scale" : "2x"
        }
         ......
         ......
        {
          "size" : "60x60",
          "idiom" : "iphone",
          "filename" : "[email protected]",
          "scale" : "3x"
        }
      ],
      "info" : {
        "version" : 1,
        "author" : "xcode"
      }
    }
    

    I want to iterate through each dictionary in images array. For that I wrote

    declare -a images=($(cat Contents.json | jq ".images[]"))
    for image in "${images[@]}"
    do
        echo "image --$image"
    done
    

    I am expecting output that each dictionary is printing in an iteration. That is

    image --{
      "size" : "29x29",
      "idiom" : "iphone",
      "filename" : "[email protected]",
      "scale" : "2x"
    }
    image --{
      "size" : "29x29",
      "idiom" : "iphone",
      "filename" : "[email protected]",
      "scale" : "3x"
    }
    image --{
      "size" : "40x40",
      "idiom" : "iphone",
      "filename" : "[email protected]",
      "scale" : "2x"
    }
    

    Etc

    But its iterating through each and every single elements in each dictionary like

    image --{
    image --"size":
    image --"29x29",
    image --"idiom":
    image --"iphone",
    image --"filename":
    ....
    ....
    ....
    

    What is wrong with my code

  • Johnykutty
    Johnykutty over 8 years
    The code is working perfectly image -- is used just for distinguishing new lines wile printing
  • Johnykutty
    Johnykutty over 8 years
    One more doubt, how can I separate each keys from the imageDict
  • Johnykutty
    Johnykutty over 8 years
    I tried jq -r '.images|keys[]' $contentsPath | while read key ; do imageDict=$(jq ".images[$key]" $contentsPath) echo "image --$imageDict" echo "filename --"$("$imageDict" | jq ".filename" ) done but its not working @hek2ml
  • Johnykutty
    Johnykutty over 8 years
    instead of printing whole elements how can I print specific value for key sy filename in the loop
  • Johnykutty
    Johnykutty over 8 years
    +1 Working as expected :) a small side question. if we use like this(the while read method), is it reading from file each time or reading only once?
  • Johnykutty
    Johnykutty over 8 years
    each time in the sense in each iteration
  • hek2mgl
    hek2mgl over 8 years
    Each time. That's the drawback. I would use the jq-only solution I've suggested.
  • Johnykutty
    Johnykutty over 8 years
  • Johnykutty
    Johnykutty over 8 years
    I also like reading only one, but my need is to read 2-3keys from the dictionary and process them. Anywyay this is working fine