465

I have the following json file:

{
    "FOO": {
        "name": "Donald",
        "location": "Stockholm"
    },
    "BAR": {
        "name": "Walt",
        "location": "Stockholm"
    },
    "BAZ": {
        "name": "Jack",
        "location": "Whereever"
    }
}

I am using jq and want to get the "name" elements of the objects where 'location' is 'Stockholm'.

I know I can get all names by

cat json | jq .[] | jq ."name"
"Jack"
"Walt"
"Donald"

But I can't figure out how to print only certain objects, given the value of a sub key (here: "location" : "Stockholm").

syntagma
  • 23,346
  • 16
  • 78
  • 134
Daniel
  • 12,445
  • 4
  • 21
  • 18

5 Answers5

667

Adapted from this post on Processing JSON with jq, you can use the select(bool) like this:

$ jq '.[] | select(.location=="Stockholm")' json
{
  "location": "Stockholm",
  "name": "Walt"
}
{
  "location": "Stockholm",
  "name": "Donald"
}
KyleMit
  • 30,350
  • 66
  • 462
  • 664
Daniel
  • 12,445
  • 4
  • 21
  • 18
  • 51
    How would I get the parent 'FOO', 'BAR', 'BAZ'? – spazm Jun 15 '16 at 18:13
  • 1
    I'm not sure what you mean by parent (do you mean key?)... it's just `jq 'keys' json`. If you meant the keys after the filter, giving `"FOO" "BAR"`, use [this answer](https://stackoverflow.com/a/31911811/6243352) and use `.key` instead of `[.key, .value.name]`. – ggorlen Sep 12 '20 at 04:26
  • why aren't the commas preserved between the elements? – Essex Boy Mar 20 '23 at 10:55
306

To obtain a stream of just the names:

$ jq '.[] | select(.location=="Stockholm") | .name' json

produces:

"Donald"
"Walt"

To obtain a stream of corresponding (key name, "name" attribute) pairs, consider:

$ jq -c 'to_entries[]
        | select (.value.location == "Stockholm")
        | [.key, .value.name]' json

Output:

["FOO","Donald"]
["BAR","Walt"]
peak
  • 105,803
  • 17
  • 152
  • 177
  • 1
    He wants the whole object based on location: "I can't figure out how to print only certain objects, given the value of a sub key" – Fo. May 30 '16 at 16:46
  • 3
    You don't need the pipe after selection: $ jq '.[] | select(.location=="Stockholm").name' json – Deepak Apr 04 '18 at 13:22
  • Making the `name` key variable (use a shell function with `$1` as parameter) does not work: `termux-contact-list |jq -r '.[] | select(.name=="$1")|.number'`. I call it like `cool_fn Name1`. However, this works: `termux-contact-list |jq -r '.[] | select(.name=="Name1")|.number'` – Timo Aug 18 '18 at 08:13
  • [Here](https://stackoverflow.com/a/40027637/1705829) is the solution if you like it variable. – Timo Aug 19 '18 at 07:13
  • 1
    @Fo - In the body of the question, the OP was quite specific: 'I want to get the "name" elements of the objects'. Evidently, in the title of the Q, the OP was using the word "objects" in a generic sense. – peak Aug 12 '20 at 18:42
  • Is there a way sort the output i.e. `["FOO","Donald"] ["BAR","Walt"]` as `["BAR","Walt"] ["FOO","Donald"]` – Sandeep Kanabar Sep 24 '20 at 14:36
  • Yes. Use `sort` or `sort_by`. – peak Sep 24 '20 at 20:09
  • Thanks, is there a way to add prefix to the selected KEY? e.g, add `MY_` as prefix to the parent name, say change `FOO` to `MY_FOO` in the above example. – rodee Oct 01 '20 at 12:36
  • @rodee - Yes, it’s trivial. Use string addition on `.key` – peak Oct 01 '20 at 15:59
57

I had a similar related question: What if you wanted the original object format back (with key names, e.g. FOO, BAR)?

Jq provides to_entries and from_entries to convert between objects and key-value pair arrays. That along with map around the select

These functions convert between an object and an array of key-value pairs. If to_entries is passed an object, then for each k: v entry in the input, the output array includes {"key": k, "value": v}.

from_entries does the opposite conversion, and with_entries(foo) is a shorthand for to_entries | map(foo) | from_entries, useful for doing some operation to all keys and values of an object. from_entries accepts key, Key, name, Name, value and Value as keys.

jq15 < json 'to_entries | map(select(.value.location=="Stockholm")) | from_entries'

{
  "FOO": {
    "name": "Donald",
    "location": "Stockholm"
  },
  "BAR": {
    "name": "Walt",
    "location": "Stockholm"
  }
}

Using the with_entries shorthand, this becomes:

jq15 < json 'with_entries(select(.value.location=="Stockholm"))'
{
  "FOO": {
    "name": "Donald",
    "location": "Stockholm"
  },
  "BAR": {
    "name": "Walt",
    "location": "Stockholm"
  }
}
spazm
  • 4,399
  • 31
  • 30
  • 5
    One thing that keeps biting me is that you need to remember that when using `with_entries()`, you usually want to also use `.value` in the `select` clause. This is because the `to_entries` macro converts the given entries to `.key` and `.value` pair, which also happens with `with_entries`. – Jaakko Mar 28 '19 at 06:31
35

Just try this one as a full copy paste in the shell and you will grasp it.

# pass the multiline string to the jq, use the jq to 
# select the attribute named "card_id" 
# ONLY if its neighbour attribute
# named "card_id_type" has the "card_id_type-01" value.
# jq -r means give me ONLY the value of the jq query no quotes aka raw


cat << EOF | \
    jq -r '.[]| select (.card_id_type == "card_id_type-01")|.card_id'
    [  
     { "card_id": "id-00", "card_id_type": "card_id_type-00"},
     { "card_id": "id-01", "card_id_type": "card_id_type-01"},
     { "card_id": "id-02", "card_id_type": "card_id_type-02"}
    ]
EOF
# this ^^^ MUST start first on the line - no whitespace there !!!
# outputs:
# id-01

or with an aws cli command

 # list my vpcs or
 # list the values of the tags which names are "Name" 
 aws ec2 describe-vpcs | jq -r '.| .Vpcs[].Tags[]
        |select (.Key == "Name") | .Value'|sort  -nr

Note that you could move up and down in the hierarchy both during the filtering phase and during the selecting phase :

 kubectl get services --all-namespaces -o json | jq -r '
 .items[] | select( .metadata.name 
     | contains("my-srch-string")) | 
     { name: .metadata.name, ns: .metadata.namespace 
     , nodePort: .spec.ports[].nodePort
     , port: .spec.ports[].port}
 '
Yordan Georgiev
  • 5,114
  • 1
  • 56
  • 53
0

Hopefully this helps others, ive used jq in 100s of my own scripts, but i fight with using vars in it every single time... in this case below was only way i could get a number var to pass to it (ie single quotes around the arg/var):

id=26533

cat mydata.json | jq -r --arg id "$id" '.sensor[] |
select(.objid=='$id').probe'
James Gaul
  • 57
  • 5