Add support for .[]

js-version
Anton Medvedev 4 years ago
parent d455165d25
commit 19a9d34dad

@ -81,40 +81,35 @@ One of the frequent operations is mapping some function on an array. For example
{ {
"author": { "author": {
"name": "antonmedv" "name": "antonmedv"
}, }
...
}, },
{...}, {...},
{...},
... ...
] ]
``` ```
And we want to collect names of each object in array. We can do this by mapping anonymous function: And we want to collect names of each object in the array. We can do this by mapping anonymous function:
```bash ```bash
$ cat ... | fx '.map(x => x.author.name)' $ cat ... | fx '.map(x => x.author.name)'
``` ```
Or we can do the same by using `@` prefix: Or we can do the same by using jq-like syntax:
```bash ```bash
$ cat ... | fx @.author.name $ cat ... | fx .[].author.name
[ [
"antonmedv", "antonmedv",
... ...
] ]
``` ```
Expression followed by `@` symbol will be mapped to each element of array. > Note what `[]` can be applied to map object values.
> Note what `@` can be applied to map object values.
> ```bash > ```bash
> $ echo '{"foo": 1, "bar": 2}' | fx @+1 > $ echo '{"foo": 1, "bar": 2}' | fx .[]
> [2, 3] > [1, 2]
> ``` > ```
>
> Also note what symbol `@` alone is equivalent of `Object.values` function.
### Chaining ### Chaining

@ -27,7 +27,7 @@ brew install fx
``` ```
Or download standalone binary from [releases](https://github.com/antonmedv/fx/releases) Or download standalone binary from [releases](https://github.com/antonmedv/fx/releases)
```bash ```bash
bash <( curl -L https://fx.wtf ) bash <( curl https://fx.wtf )
``` ```
## Usage ## Usage
@ -65,6 +65,11 @@ $ echo '{"count": 0}' | fx '{...this, count: 1}'
} }
``` ```
Extract values from maps.
```bash
$ fx commits.json | fx .[].author.name
```
Print formatted JSON to stdout. Print formatted JSON to stdout.
```bash ```bash
$ curl ... | fx . $ curl ... | fx .

@ -1,75 +0,0 @@
#!/usr/bin/env bash
set -e
set -u
set -o pipefail
put() {
echo -e "\e[1;32m$1\e[0m"
}
do_install() {
cat <<'EOM'
______ _____ _ _ _
| ____| |_ _| | | | | |
| |____ __ | | _ __ ___| |_ __ _| | | ___ _ __
| __\ \/ / | | | '_ \/ __| __/ _` | | |/ _ \ '__|
| | > < _| |_| | | \__ \ || (_| | | | __/ |
|_| /_/\_\ |_____|_| |_|___/\__\__,_|_|_|\___|_|
EOM
platform=''
if [[ "$OSTYPE" == "linux"* ]]; then
platform='linux'
elif [[ "$OSTYPE" == "darwin"* ]]; then
platform='macos'
elif [[ "$OSTYPE" == "win"* ]]; then
platform='win.exe'
fi
if test "x$platform" = "x"; then
cat <<EOM
/=====================================\\
| COULD NOT DETECT PLATFORM |
\\=====================================/
Uh oh! We couldn't automatically detect your operating system.
Please, download fx from https://github.com/antonmedv/fx/releases manually.
EOM
exit 2
else
put "Detected platform: $platform"
fi
put "Downloading latest fx-$platform.zip"
curl -L "https://github.com/antonmedv/fx/releases/latest/download/fx-$platform.zip" > "fx-$platform.zip"
put "Extracting fx-$platform.zip"
unzip "fx-$platform.zip"
rm "fx-$platform.zip"
mv "fx-$platform" fx
name=''
read -p $'\e[1;35mWhould you like to move fx binary to /usr/local/bin?\e[0m [Y/n] ' -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
mv fx /usr/local/bin/fx
name='fx'
else
name='./fx'
fi
cat <<EOM
Now you can start using fx.
$ $name '.downloadCount + 1'
EOM
curl -sAx http://bit.ly/downloads-count > /dev/null
}
do_install

@ -46,8 +46,8 @@
"string-width": "^4.2.0" "string-width": "^4.2.0"
}, },
"devDependencies": { "devDependencies": {
"ava": "^3.9.0", "ava": "^3.12.1",
"pkg": "^4.4.8", "pkg": "^4.4.9",
"release-it": "^13.6.3" "release-it": "^14.0.1"
} }
} }

@ -13,10 +13,16 @@ function reduce(json, code) {
return Object.keys(json) return Object.keys(json)
} }
if (/^@/.test(code)) { if (/^(\.\w*)+\[]/.test(code)) {
return eval(`function fn() { function fold(s) {
return Object.values(this).map(x => x${code.substring(1)}) if (s.length === 1) {
}; fn`).call(json) return 'x => x' + s[0]
}
let obj = s.shift()
obj = obj === '.' ? 'x' : 'x' + obj
return `x => Object.values(${obj}).flatMap(${fold(s)})`
}
code = fold(code.split('[]'))
} }
if (/^\.\[/.test(code)) { if (/^\.\[/.test(code)) {

@ -1,5 +1,4 @@
'use strict' 'use strict'
const fs = require('fs')
const skip = Symbol('skip') const skip = Symbol('skip')
@ -25,7 +24,7 @@ function save(json) {
if (!global.FX_FILENAME) { if (!global.FX_FILENAME) {
throw "No filename provided.\nTo edit-in-place, specify JSON file as first argument." throw "No filename provided.\nTo edit-in-place, specify JSON file as first argument."
} }
fs.writeFileSync(global.FX_FILENAME, JSON.stringify(json, null, 2)) require('fs').writeFileSync(global.FX_FILENAME, JSON.stringify(json, null, 2))
return json return json
} }

@ -8,32 +8,32 @@ function fx(json, code = '') {
} }
test('pass', t => { test('pass', t => {
const r = fx([{"greeting": "hello world"}]) const r = fx([{'greeting': 'hello world'}])
t.deepEqual(JSON.parse(r), [{"greeting": "hello world"}]) t.deepEqual(JSON.parse(r), [{'greeting': 'hello world'}])
}) })
test('anon func', t => { test('anon func', t => {
const r = fx({"key": "value"}, "'function (x) { return x.key }'") const r = fx({'key': 'value'}, '\'function (x) { return x.key }\'')
t.is(r, 'value\n') t.is(r, 'value\n')
}) })
test('arrow func', t => { test('arrow func', t => {
const r = fx({"key": "value"}, "'x => x.key'") const r = fx({'key': 'value'}, '\'x => x.key\'')
t.is(r, 'value\n') t.is(r, 'value\n')
}) })
test('arrow func ()', t => { test('arrow func ()', t => {
const r = fx({"key": "value"}, "'(x) => x.key'") const r = fx({'key': 'value'}, '\'(x) => x.key\'')
t.is(r, 'value\n') t.is(r, 'value\n')
}) })
test('this bind', t => { test('this bind', t => {
const r = fx([1, 2, 3, 4, 5], "'this.map(x => x * this.length)'") const r = fx([1, 2, 3, 4, 5], '\'this.map(x => x * this.length)\'')
t.deepEqual(JSON.parse(r), [5, 10, 15, 20, 25]) t.deepEqual(JSON.parse(r), [5, 10, 15, 20, 25])
}) })
test('chain', t => { test('chain', t => {
const r = fx({"items": ["foo", "bar"]}, "'this.items' '.' 'x => x[1]'") const r = fx({'items': ['foo', 'bar']}, '\'this.items\' \'.\' \'x => x[1]\'')
t.is(r, 'bar\n') t.is(r, 'bar\n')
}) })
@ -73,3 +73,13 @@ test('lossless number', t => {
const r = execSync(`echo '{"long": 123456789012345678901}' | node index.js .long`).toString('utf8') const r = execSync(`echo '{"long": 123456789012345678901}' | node index.js .long`).toString('utf8')
t.is(r, '123456789012345678901\n') t.is(r, '123456789012345678901\n')
}) })
test('value iterator', t => {
const r = fx({master: {foo: [{bar: [{val: 1}]}]}}, '.master.foo[].bar[].val')
t.deepEqual(JSON.parse(r), [1])
})
test('value iterator simple', t => {
const r = fx([{val:1},{val:2}], '.[].val')
t.deepEqual(JSON.parse(r), [1, 2])
})

Loading…
Cancel
Save