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": {
"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
$ 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
$ cat ... | fx @.author.name
$ cat ... | fx .[].author.name
[
"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
> $ echo '{"foo": 1, "bar": 2}' | fx @+1
> [2, 3]
> $ echo '{"foo": 1, "bar": 2}' | fx .[]
> [1, 2]
> ```
>
> Also note what symbol `@` alone is equivalent of `Object.values` function.
### Chaining

@ -27,7 +27,7 @@ brew install fx
```
Or download standalone binary from [releases](https://github.com/antonmedv/fx/releases)
```bash
bash <( curl -L https://fx.wtf )
bash <( curl https://fx.wtf )
```
## 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.
```bash
$ 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"
},
"devDependencies": {
"ava": "^3.9.0",
"pkg": "^4.4.8",
"release-it": "^13.6.3"
"ava": "^3.12.1",
"pkg": "^4.4.9",
"release-it": "^14.0.1"
}
}

@ -13,10 +13,16 @@ function reduce(json, code) {
return Object.keys(json)
}
if (/^@/.test(code)) {
return eval(`function fn() {
return Object.values(this).map(x => x${code.substring(1)})
}; fn`).call(json)
if (/^(\.\w*)+\[]/.test(code)) {
function fold(s) {
if (s.length === 1) {
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)) {

@ -1,5 +1,4 @@
'use strict'
const fs = require('fs')
const skip = Symbol('skip')
@ -25,7 +24,7 @@ function save(json) {
if (!global.FX_FILENAME) {
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
}

@ -8,32 +8,32 @@ function fx(json, code = '') {
}
test('pass', t => {
const r = fx([{"greeting": "hello world"}])
t.deepEqual(JSON.parse(r), [{"greeting": "hello world"}])
const r = fx([{'greeting': 'hello world'}])
t.deepEqual(JSON.parse(r), [{'greeting': 'hello world'}])
})
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')
})
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')
})
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')
})
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])
})
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')
})
@ -73,3 +73,13 @@ test('lossless number', t => {
const r = execSync(`echo '{"long": 123456789012345678901}' | node index.js .long`).toString('utf8')
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