jq is the most popular command line processing JSON utility, written in C and has its own syntax for working with JSON.
However, processing JSON on the command line is not necessary very often, and when the need arises, you have to suffer with an unfamiliar programming language.
So the idea to write fx with a simple and clear syntax that you will never forget. And what programming language do everyone know? That's right - javascript.
fx takes as its argument a javascript code containing an anonymous function, and calls it, substituting as an argument the JSON obtained from stdin. What the function returns will be output to stdout. Everything. No more complicated and incomprehensible designs.
Compare two solutions of the same problem on jq and on fx:
jq '[.order[] as $x | .data[$x]]'
fx 'input => input.order.map(x => obj.data[x])'
A little more verbose? Yes, it's just plain javascript.
If fx doesn't pass arguments at all, then JSON will be formatted and output:
$ echo '{"key":"value"}' | fx { "key": "value" }
If the code does not contain param =>
, then the transferred code will be automatically converted to an anonymous function and this
will contain the transferred JSON object:
$ echo '{"foo": [{"bar": "value"}]}' | fx 'this.foo[0].bar' "value"
fx, you can pass several arguments / anonymous functions, they will be applied alternately to JSON:
$ echo '{"foo": [{"bar": "value"}]}' | fx 'x => x.foo' 'this[0]' 'this.bar' "value"
If the code contains the yield
keyword, the anonymous function created will contain a “unwrapped” generator (example from a generator-expression ):
$ cat data.json | fx '\ for (let user of this) \ if (user.login.startsWith("a")) \ yield user'
$ echo '["a", "b"]' | fx 'yield* this' [ "a", "b" ]
This allows you to describe very simple samples in complex queries.
By the way, modifying JSON with fx is also very simple:
$ echo '{"count": 0}' | fx '{...this, count: 1}' { "count": 1 }
You can also use any npm package if you put it globally:
$ npm install -g lodash $ cat package.json | fx 'require("lodash").keys(this.dependencies)'
For some, it is important that jq is just one binary (~ 2mb). So fx also has separate binaries .
They weigh a little more (~ 30mb), but if this is not critical for you, and it costs nodejs, you can install fx using npm:
npm install -g fx
And what about performance? Let's measure with hyperfine :
$ curl 'https://api.github.com/repos/stedolan/jq/commits' > data.json $ hyperfine --warmup 3 "jq ..." "fx ..." Benchmark #1: cat data.json | jq '[.[] | {message: .commit.message, name: .commit.committer.name}]' Time (mean ± σ): 10.7 ms ± 0.9 ms [User: 8.7 ms, System: 3.6 ms] Range (min … max): 9.0 ms … 14.9 ms Benchmark #2: cat data.json | fx 'this.map(({commit}) => ({message: commit.message, name: commit.committer.name}))' Time (mean ± σ): 159.6 ms ± 4.4 ms [User: 127.4 ms, System: 28.4 ms] Range (min … max): 153.0 ms … 170.0 ms
An order of magnitude less. And the thing is that fx uses eval
to run the code from the arguments (and in general all the code fx <70 lines of code). If processing speed is important to you, do not use fx. In all other cases - fx is an excellent choice.
I hope this utility is useful to someone. Thanks for attention.
Source: https://habr.com/ru/post/347808/
All Articles