jq: How do I reshape into an object, using multiple selects as values?

Viewed 228

JSON

[
      {
        "name": "username",
        "value": "my-username"
      },
      {
        "name": "password",
        "value": "my-password"
      }
]

Desired Result

{ "username": "my-username", "password": "my-password" }

A few failed attempts

.[] | { username: select(.name == "username").value, password: select(.name == "password").value }

No output

.[] | { username: select(.name == "username").value} + { password: select(.name == "password").value }

No output

.[] | { username: select(.name == "username").value } + .[] | { password: select(.name == "password").value }

Fails

If I only want a single field, the syntax works fine. E.g.

.[] | { username: select(.name == "username").value }

produces

{ "username": "my-username" }

The only issue is trying to do the same with multiple elements.

Thoughts? Thanks!

2 Answers

If your expression starts with .[] | and then the thing after the | produces an object, then you must be producing 0 or 1 objects per element of the array. But you know that that's not what you want. You want one object, so you should be starting by producing an object.

{ 
    username: (.[] | select(.name == "username").value), 
    password: (.[] | select(.name == "password").value) 
}

is similar to what you're currently doing, but it works.

You could also do map({ key: .name, value }) | from_entries as a nicer way to do the same thing: it uses jq's builtin from_entries to turn an array of key/value pairs into an object. You just have to change your name key into key, which is what from_entries expects.

As it happens, jq (version 1.5 or later) already understands the {"name":_, "value":_} method of representing a JSON object, so you can achieve the result simply using the jq filter:

from_entries
Related