39

I'm starting with json that looks like this:

{
  "object": "list",
  "data": [
    {
      "id": "in_1HW85aFGUwFHXzvl8wJbW7V7",
      "object": "invoice",
      "account_country": "US",
      "customer_name": "clientOne",
      "date": 1601244686,
      "livemode": true,
      "metadata": {},
      "paid": true,
      "status": "paid",
      "total": 49500
    },
    {
      "id": "in_1HJlIZFGUwFHXzvlWqhegRkf",
      "object": "invoice",
      "account_country": "US",
      "customer_name": "clientTwo",
      "date": 1598297143,
      "livemode": true,
      "metadata": {},
      "paid": true,
      "status": "paid",
      "total": 51000
    },
    {
      "id": "in_1HJkg5FGUwFHXzvlYp2uC63C",
      "object": "invoice",
      "account_country": "US",
      "customer_name": "clientThree",
      "date": 1598294757,
      "livemode": true,
      "metadata": {},
      "paid": true,
      "status": "paid",
      "total": 57000
    },
    {
      "id": "in_1H8B0pFGUwFHXzvlU6nrOm6I",
      "object": "invoice",
      "account_country": "US",
      "customer_name": "clientThree",
      "date": 1595536051,
      "livemode": true,
      "metadata": {},
      "paid": true,
      "status": "paid",
      "total":  20000
    }
  ],
  "has_more": true,
  "url": "/v1/invoices"
}

If I do

cat sample.json | jq -C '.data[] | {invoice_id: .id, date: .date | strftime("%Y-%m-%d"), amount: .total} | .amount = "$" + (.amount/100|tostring)'

I can successfully tidy this up (the actual data is far more verbose, hundreds of lines to eliminate) and so that gives me:

{
  "invoice_id": "in_1HW85aFGUwFHXzvl8wJbW7V7",
  "date": "2020-09-27",
  "amount": "$495"
}
{
  "invoice_id": "in_1HJlIZFGUwFHXzvlWqhegRkf",
  "date": "2020-08-24",
  "amount": "$510"
}
{
  "invoice_id": "in_1HJkg5FGUwFHXzvlYp2uC63C",
  "date": "2020-08-24",
  "amount": "$570"
}
{
  "invoice_id": "in_1H8B0pFGUwFHXzvlU6nrOm6I",
  "date": "2020-07-23",
  "amount": "$200"
}

But that's in the wrong order. I want to sort by the date field, so that the most recent item is displayed last at the bottom.

I've tried every wrong thing imaginable. How do I apply sort_by(.date) to this? I keep getting cannot index string with string "date" errors (and miscellaneous others, but mostly that one).

5
  • 2
    Do people not call out 'Useless Use of Cat' anymore? jq [options...] filter [files...] Mar 7, 2023 at 18:21
  • 2
    @geedoubleya I often cat the output of curl calls that I've saved to disc to jq and when I'm working on the jq string, I want the last argument to be my quoted jq script. Otherwise I have to keep re-typing or moving over the filename in the shell. Yuck. Jun 28, 2023 at 23:24
  • 1
    > Do people not call out 'Useless Use of Cat' anymore? I would be happy to hear that this meme has died! It's an anti-pattern - as @synthesizerpatel points out, when iterating and experimenting in the shell, it's both neater and more intuitive to start the pipeline-of-commands with cat file.txt
    – scubbo
    Oct 24, 2023 at 19:15
  • @scubbo completely disagree with you. It's not an anti-pattern. It's far better to use system tools correctly and using STDIN is NOT intuitive in my subjective opinion. Objectively, there is absolutely no reason to use cat and @geedoubleya is absolutely correct. TBH, I don't even understand what @synthesizerpatel is trying to articulate in order to justify using it. But hey, if someone wants to type useless stuff and waste time doing it, I guess that's their prerogative. Let's stay away from cheering on poor practice please.
    – Jim
    Jan 20 at 17:48
  • cat file | map | filter is only three characters more than <file map | filter, and it avoids the awkward implication that data is flowing "in the opposite direction to" the arrow. Perfectly reasonable to me. Performance-purists might rightly identify that there is overhead of another process, but this formulation is intended for experimentation (while writing and developing a utility-pipeline or exploring a dataset), not for high-performance production use.
    – scubbo
    Jan 22 at 16:48

2 Answers 2

49

from man jq

sort, sort_by(path_expression) The sort functions sorts its input, which must be an array.

In general and invoking a separate jq command, you have to use -s, --slurp that will make these sequential objects an array, and then you can sort it by a key.

... | jq -s 'sort_by(.date)'

Now, if you have a selection already and you want that result to be an array, then I guess wrapping it all with brackets will make it:

jq '[ <some_existing_selection> ] | sort_by(.date)' file.json

example

For the json you are starting with, assuming that initially you are doing something like this (producing a sequence of objects):

jq '.data[] | {id: .id, date: .date}' file.json

you have to wrap the whole jq selection in brackets to make it an array:

jq '[.data[] | {id: .id, date: .date}]' file.json

and now this array can be sorted:

jq '[.data[] | {id: .id, date: .date}] | sort_by(.date)' file.json
1
  • It may be worth changing the date object name to be different from the date value to distinguish between what you are sorting by (object name). Mar 7, 2023 at 18:51
-1

Sample JSON :

{"first_name":"Savannah","last_name":"Williams"}
{"first_name":"Aria","last_name":"Acevedo"}
{"first_name":"Emma","last_name":"Puckett"}
{"first_name":"Claire","last_name":"Mathis"}
{"first_name":"Ryder","last_name":"Wong"}

Sort it by a field:

cat sample.json | jq -s -c 'sort_by(.first_name) | .[]'

I got it from here:

Source website

1
  • This does not seem to be related to the question at hand. Note that the user in the question has data in an array in a sub-object of their top-level object.
    – Kusalananda
    May 29, 2023 at 9:45

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .