Datium
Datium

Reputation: 93

How can I output a json with the new key name from the value in an existing json by jq

I have an existing json which have a from like this:

{
  "arg1": "Admin",
  "arg2": 0,
  "data": [
    {
      "arg3": "11",
      "user": "user1",
      "age": 51,
      "arg4": "11"
    },
    {
      "arg3": "22",
      "user": "user2",
      "age": 52,
      "arg4": "22"
    },
    {
      "arg3": "33",
      "user": "user3",
      "age": 53,
      "arg4": "33"
    },
    {
      "arg3": "44",
      "user": "user4",
      "age": 54,
      "arg4": "44"
    }
  ]
}

Then I try this command:

$ cat tmp.json|jq '.data|.[]|{user,age}'
{
  "user": "user1",
  "age": 51,
}
{
  "user": "user2",
  "age": 52,
}
{
  "user": "user3",
  "age": 53,
}
{
  "user": "user4",
  "age": 54,
}

What I expect to output is:

{
  "department": "Admin",
  "user_age": {
    "user1": "51",
    "user2": "52",
    "user3": "53",
    "user4": "54"
  },
  "year": 2016
}

In jq's manual, my request is close to example 23.

So I tried to use from_entries function

cat tmp.json|jq '.data|.[]|{user,age}|from_entries'

but get this error:

jq: error (at :30): Cannot index string with string "key"

I know this is because of its format not equal an entry.

So, what should I do to convert to the expected output?

Upvotes: 0

Views: 1085

Answers (3)

jq170727
jq170727

Reputation: 14625

As Jeff explained, from_entries expects input of the form

[ {key:xxx, value:yyy} ]

so Hao's filter

.data | .[] | {user,age} | from_entries

requires two small modifications to generate the desired user_age object:

  • the values must be {key,value} objects
  • the values need to be collected into an array

e.g.

.data | [.[]|{key:user, value:age|tostring}] | from_entries

(we include tostring because the values in the example output are strings)

But since [ .[]| ... ] is the same as map(...) this could be written

.data | map({key:user, value:age|tostring}) | from_entries

Here is a complete solution:

{
  "department": .arg1,
  "user_age": .data | map({key:.user, value:.age|tostring}) | from_entries,
  "year": 2016
}

Sample output

{
  "department": "Admin",
  "user_age": {
    "user1": "51",
    "user2": "52",
    "user3": "53",
    "user4": "54"
  },
  "year": 2016
}

Upvotes: 0

Xavi
Xavi

Reputation: 189

you can also try:

cat tmp.json|jq '{department: .arg1, user_age: (.data|map({(.user): .age})|add), year: 2016}'

or

cat tmp.json|jq '{department: .arg1, user_age: .data|map({(.user): .age})|add, year: 2016}'

Upvotes: 1

Jeff Mercado
Jeff Mercado

Reputation: 134811

from_entries expects an array of objects with key and value properties. You are however generating separate objects with user and age properties which just would not work. First of all, you would need to make sure that the properties are in an array, then have the separate key/value pairs for each of the values you want in the array.

With that said, you don't really need to use entries to build out the result, it's not the right tool for the job. You just want to pull some properties from each of the data objects and add to another object mapping users to ages. There's many ways this can be achieved, I would use reduce to accomplish this.

reduce .data[] as $d ({}; .[$d.user] = $d.age)

To get your final result, just combine the parts you need into the object you're requiring. I'm not sure where you got that 2016 from, I'm assuming it's just hardcoded.

{department: .arg1, user_age: (reduce .data[] as $d ({}; .[$d.user] = $d.age)), year: 2016}

Upvotes: 1

Related Questions