mirror of
https://github.com/nushell/nushell.git
synced 2025-06-08 19:17:16 +02:00
# Description `recurse` command is similar to `jq`'s `recurse`/`..` command. Along with values, it also returns their cell-paths relative to the "root" (initial input) By default it uses breadth-first traversal, collecting child items of all available sibling items before starting to process those child items. This means output is ordered in increasing depth. With the `--depth-first` flag it uses a stack based recursive descend, which results in output order identical to `jq`'s `recurse`. It can be used in the following ways: - `... | recurse`: Recursively traverses the input value, returns each value it finds as a stream. - `... | recurse foo.bar`: Only descend through the given cell-path. - `... | recurse {|parent| ... }`: Produce child values with a closure. ```nushell { "foo": { "egg": "X" "spam": "Y" } "bar": { "quox": ["A" "B"] } } | recurse | update item { to nuon } # => ╭───┬──────────────┬───────────────────────────────────────────────╮ # => │ # │ path │ item │ # => ├───┼──────────────┼───────────────────────────────────────────────┤ # => │ 0 │ $. │ {foo: {egg: X, spam: Y}, bar: {quox: [A, B]}} │ # => │ 1 │ $.foo │ {egg: X, spam: Y} │ # => │ 2 │ $.bar │ {quox: [A, B]} │ # => │ 3 │ $.foo.egg │ "X" │ # => │ 4 │ $.foo.spam │ "Y" │ # => │ 5 │ $.bar.quox │ [A, B] │ # => │ 6 │ $.bar.quox.0 │ "A" │ # => │ 7 │ $.bar.quox.1 │ "B" │ # => ╰───┴──────────────┴───────────────────────────────────────────────╯ {"name": "/", "children": [ {"name": "/bin", "children": [ {"name": "/bin/ls", "children": []}, {"name": "/bin/sh", "children": []}]}, {"name": "/home", "children": [ {"name": "/home/stephen", "children": [ {"name": "/home/stephen/jq", "children": []}]}]}]} | recurse children | get item.name # => ╭───┬──────────────────╮ # => │ 0 │ / │ # => │ 1 │ /bin │ # => │ 2 │ /home │ # => │ 3 │ /bin/ls │ # => │ 4 │ /bin/sh │ # => │ 5 │ /home/stephen │ # => │ 6 │ /home/stephen/jq │ # => ╰───┴──────────────────╯ {"name": "/", "children": [ {"name": "/bin", "children": [ {"name": "/bin/ls", "children": []}, {"name": "/bin/sh", "children": []}]}, {"name": "/home", "children": [ {"name": "/home/stephen", "children": [ {"name": "/home/stephen/jq", "children": []}]}]}]} | recurse children --depth-first | get item.name # => ╭───┬──────────────────╮ # => │ 0 │ / │ # => │ 1 │ /bin │ # => │ 2 │ /bin/ls │ # => │ 3 │ /bin/sh │ # => │ 4 │ /home │ # => │ 5 │ /home/stephen │ # => │ 6 │ /home/stephen/jq │ # => ╰───┴──────────────────╯ 2 | recurse { ({path: square item: ($in * $in)}) } | take while { $in.item < 100 } # => ╭───┬─────────────────┬──────╮ # => │ # │ path │ item │ # => ├───┼─────────────────┼──────┤ # => │ 0 │ $. │ 2 │ # => │ 1 │ $.square │ 4 │ # => │ 2 │ $.square.square │ 16 │ # => ╰───┴─────────────────┴──────╯ ``` # User-Facing Changes No changes other than the new command. # Tests + Formatting Added tests for examples. (As we can't run them directly as tests yet.) - 🟢 `toolkit test stdlib` # After Submitting - Update relevant parts of https://www.nushell.sh/cookbook/jq_v_nushell.html - `$env.config | recurse | where ($it.item | describe -d).type not-in [list, record, table]` can partially cover the use case of `config flatten`, should we do something? --------- Co-authored-by: Bahex <17417311+Bahex@users.noreply.github.com>
82 lines
2.0 KiB
Plaintext
82 lines
2.0 KiB
Plaintext
use std/assert
|
|
use std/testing *
|
|
use std-rfc/iter *
|
|
|
|
@test
|
|
def recurse-example-basic [] {
|
|
let out = {
|
|
"foo": {
|
|
"egg": "X"
|
|
"spam": "Y"
|
|
}
|
|
"bar": {
|
|
"quox": ["A" "B"]
|
|
}
|
|
}
|
|
| recurse
|
|
|
|
let expected = [
|
|
[path, item];
|
|
[ ($.), {foo: {egg: X, spam: Y}, bar: {quox: [A, B]}} ],
|
|
[ ($.foo), {egg: X, spam: Y} ],
|
|
[ ($.bar), {quox: [A, B]} ],
|
|
[ ($.foo.egg), X ],
|
|
[ ($.foo.spam), Y ],
|
|
[ ($.bar.quox), [A, B] ],
|
|
[ ($.bar.quox.0), A ],
|
|
[ ($.bar.quox.1), B ]
|
|
]
|
|
|
|
assert equal $out $expected
|
|
}
|
|
|
|
@test
|
|
def recurse-example-jq [] {
|
|
let out = {"name": "/", "children": [
|
|
{"name": "/bin", "children": [
|
|
{"name": "/bin/ls", "children": []},
|
|
{"name": "/bin/sh", "children": []}]},
|
|
{"name": "/home", "children": [
|
|
{"name": "/home/stephen", "children": [
|
|
{"name": "/home/stephen/jq", "children": []}]}]}]}
|
|
| recurse children
|
|
| get item.name
|
|
|
|
let expected = [/, /bin, /home, /bin/ls, /bin/sh, /home/stephen, /home/stephen/jq]
|
|
|
|
assert equal $out $expected
|
|
}
|
|
|
|
@test
|
|
def recurse-example-jq-depth-first [] {
|
|
let out = {"name": "/", "children": [
|
|
{"name": "/bin", "children": [
|
|
{"name": "/bin/ls", "children": []},
|
|
{"name": "/bin/sh", "children": []}]},
|
|
{"name": "/home", "children": [
|
|
{"name": "/home/stephen", "children": [
|
|
{"name": "/home/stephen/jq", "children": []}]}]}]}
|
|
| recurse children --depth-first
|
|
| get item.name
|
|
|
|
let expected = [/, /bin, /bin/ls, /bin/sh, /home, /home/stephen, /home/stephen/jq]
|
|
|
|
assert equal $out $expected
|
|
}
|
|
|
|
@test
|
|
def recurse-example-closure [] {
|
|
let out = 2
|
|
| recurse { ({path: square item: ($in * $in)}) }
|
|
| take while { $in.item < 100 }
|
|
|
|
let expected = [
|
|
[path, item];
|
|
[$., 2],
|
|
[$.square, 4],
|
|
[$.square.square, 16]
|
|
]
|
|
|
|
assert equal $out $expected
|
|
}
|