How to get max value from JSON?
Asked Answered
O

3

10

There is a json file like this:

[
{
    "createdAt": 1548729542000,
    "platform": "foo"
},
{
    "createdAt": 1548759398000,
    "platform": "foo"
},
{
    "createdAt": 1548912360000,
    "platform": "foo"
},
{
    "createdAt": 1548904550000,
    "platform": "bar"
}
]

Now I want to get the max createdAt of foo platform? how to implement it by using jq?

jq '.[] | select(.platform=="foo") | .createdAt | max' foo.json
jq: error (at <stdin>:17): number (1548729542000) and number (1548729542000) cannot be iterated over

jq '.[] | select(.platform=="foo") | max_by(.createdAt)' foo.json
jq: error (at <stdin>:17): Cannot index number with string "createdAt"
exit status 5
Offside answered 2/2, 2019 at 3:15 Comment(0)
K
19

max expects an array as input.

$ jq 'map(select(.platform == "foo") .createdAt) | max' file
1548912360000
Krouse answered 2/2, 2019 at 3:55 Comment(3)
Answer would be better if it explained more. If I take the example json and jq '.[] | select(.platform=="foo") | .createdAt' then it apparently outputs rows. map takes an array and outputs a new array. So it's not clear to me why map works here... well, the fact it works indicates that original query didn't produce an array, but I don't understand the difference.Lytton
@Lytton the difference is that .[] | select(.platform=="foo") | .createdAt yields numbers and map(select(.platform=="foo") | .createdAt) yields an array. When you do 1, 2, 3 | max, max is applied to each number (and fail because they aren't arrays); when you do [1, 2, 3] | max it is applied to the whole array (and return the element with maximum value).Krouse
Thanks, I have been working it out by trial and error... I was finding confusing the distinction between returned rows (not an array itself, but each row might be an array) and collecting rows into an array and returning that. Your answer has helped me though.Lytton
J
5

One approach would be to make the selection and then use one of the array-oriented builtins max or max_by to find the maximum, e.g.

map(select(.platform=="foo"))
| max_by(.createdAt)
| .createdAt

However, this approach is not very satisfactory as it requires more space than is strictly necessary. For large arrays, a stream-oriented version of max_by would be better.

max_by

def max_by(s; f):
  reduce s as $s (null;
    if . == null then {s: $s, m: ($s|f)}
    else  ($s|f) as $m
    | if $m > .m then {s: $s, m: $m} else . end
    end)
  | .s ;

max_by(.[] | select(.platform=="foo"); .createdAt)
| .createdAt
Janey answered 2/2, 2019 at 4:3 Comment(0)
R
0

Why not:

cat foo.json | jq -c '.[] | select(.platform=="foo") | .createdAt' | sort -un | tail -1

JSON -> list of numbers -> sort (unique) all by number, asc -> get last (max)

Rebate answered 24/3, 2024 at 8:43 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.