'How to sum up string fields that contain percentages with 'jq'?
I have a JSON file that's keeping track of column widths for a table as percentages. So the input file, columns.json, looks something like this:
[
{
"name": "Column A",
"width": "33%"
},
{
"name": "Column B",
"width": "33%"
},
{
"name": "Column C",
"width": "33%"
},
{
"name": "Column D",
"visible": false
}
]
Some columns are not displayed and therefore don't have widths (jq '.[].width' will return nulls for these), and then there's also the issue of the percent signs. Otherwise I might've used munge | munge | paste -sd+ | bc, which is usually what I use for summing things up in the shell, but that seems stupid here because jq ought to be able to do this by itself.
So using only jq, how can I sum up the width fields from this file, e.g., to make sure they don't exceed 100%?
Things I have tried (that didn't work)
I use select(.) here to filter out records that don't have a .width, then get rid of the percent sign:
jq '[.[].width | select(.) | sub("%"; "")] | add' columns.json
…but that just concatenates the strings and returns "333333".
I didn't see any mention of the word "typecast" in the jq man page, so I thought maybe it would do type inference, treating a string that looks like a number as a number in the right context:
jq '[.[].width | select(.) | sub("%"; "") | .+0] | add' columns.json
…but that just yields and error message like:
jq: error (at columns.json:18): string ("33") and number (0) cannot be added
Solution 1:[1]
This SO answer gave me the clue that there was a tostring function, so a more thorough search of the manual page revealed that the analogous function for numbers is tonumber.
Well, duh. I guess I was expecting it to be named something else, like toint, which is why I didn't find it while string-searching through the man page.
Here is the solution I ended up with:
jq 'map(.width | sub("%"; "")? | tonumber) | add' columns.json
Instead of select(.) to filter out the nulls from objects with no .width field, I just silently ignore the error in sub (with ?), which drops those records.
Note that map(.width) is just another way of saying [.[].width].
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
