From 743ccc08ee9b46703b1afaf4d77b59bf723a6d41 Mon Sep 17 00:00:00 2001 From: Douglas <32344964+NotTheDr01ds@users.noreply.github.com> Date: Thu, 3 Oct 2024 07:31:49 -0400 Subject: [PATCH] Table and list helper commands (#967) From https://github.com/nushell/nushell/issues/13887, this adds: ```nu into list select ranges select column-ranges reject ranges reject column-ranges row-indices col-indices ``` Note that these replace the former `get-row` and `get-col` commands that were in the library. They have the same functionality and can accept any combination of: * One or more row or column indices * One or more row or column ranges --- stdlib-candidate/std-rfc/conversions/into.nu | 18 ++ stdlib-candidate/std-rfc/conversions/mod.nu | 1 + stdlib-candidate/std-rfc/get-column.nu | 23 --- stdlib-candidate/std-rfc/get-row.nu | 6 - stdlib-candidate/std-rfc/mod.nu | 7 +- .../std-rfc/tables/col-indices.nu | 15 ++ stdlib-candidate/std-rfc/tables/mod.nu | 6 + .../std-rfc/tables/reject-column-ranges.nu | 13 ++ .../std-rfc/tables/reject-ranges.nu | 14 ++ .../std-rfc/tables/row-indices.nu | 25 +++ .../std-rfc/tables/select-column-ranges.nu | 20 +++ .../std-rfc/tables/select-ranges.nu | 26 +++ stdlib-candidate/tests/bulk-rename.nu | 5 +- stdlib-candidate/tests/conversions.nu | 43 +++++ stdlib-candidate/tests/mod.nu | 2 + stdlib-candidate/tests/tables.nu | 168 ++++++++++++++++++ 16 files changed, 360 insertions(+), 32 deletions(-) create mode 100644 stdlib-candidate/std-rfc/conversions/into.nu create mode 100644 stdlib-candidate/std-rfc/conversions/mod.nu delete mode 100644 stdlib-candidate/std-rfc/get-column.nu delete mode 100644 stdlib-candidate/std-rfc/get-row.nu create mode 100644 stdlib-candidate/std-rfc/tables/col-indices.nu create mode 100644 stdlib-candidate/std-rfc/tables/mod.nu create mode 100644 stdlib-candidate/std-rfc/tables/reject-column-ranges.nu create mode 100644 stdlib-candidate/std-rfc/tables/reject-ranges.nu create mode 100644 stdlib-candidate/std-rfc/tables/row-indices.nu create mode 100644 stdlib-candidate/std-rfc/tables/select-column-ranges.nu create mode 100644 stdlib-candidate/std-rfc/tables/select-ranges.nu create mode 100644 stdlib-candidate/tests/conversions.nu create mode 100644 stdlib-candidate/tests/tables.nu diff --git a/stdlib-candidate/std-rfc/conversions/into.nu b/stdlib-candidate/std-rfc/conversions/into.nu new file mode 100644 index 0000000..fe859ca --- /dev/null +++ b/stdlib-candidate/std-rfc/conversions/into.nu @@ -0,0 +1,18 @@ +# Convert a Nushell value to a list +# +# Primary useful for range-to-list, +# but other types are accepted as well. +# +# Example: +# +# 1..10 | into list +export def "into list" []: any -> list { + let input = $in + let type = ($input | describe --detailed | get type) + match $type { + range => {$input | each {||}} + list => $input + table => $input + _ => [ $input ] + } +} \ No newline at end of file diff --git a/stdlib-candidate/std-rfc/conversions/mod.nu b/stdlib-candidate/std-rfc/conversions/mod.nu new file mode 100644 index 0000000..fe7b611 --- /dev/null +++ b/stdlib-candidate/std-rfc/conversions/mod.nu @@ -0,0 +1 @@ +export use ./into.nu * \ No newline at end of file diff --git a/stdlib-candidate/std-rfc/get-column.nu b/stdlib-candidate/std-rfc/get-column.nu deleted file mode 100644 index f5e8592..0000000 --- a/stdlib-candidate/std-rfc/get-column.nu +++ /dev/null @@ -1,23 +0,0 @@ -# Documentation for get-col -# Written on 2021-03-28 06:58:50 by andras_io on discord -# Written for the new Nushell version on 03/31/2022 10:10 PM by denkspuren on discord -def get-col [ - col_index:int # A 0 indexed col_index - ] { - - # meant to be used like `ls | get-col 1` - let input = $in - let name = ($input | columns | select $col_index | get 0) - $input | select $name -} - -# Documentation for get-col2 -# Written on 2021-03-28 07:00:24 by johng on discord -# Written for the new Nushell version on 03/31/2022 10:10 PM by denkspuren on discord -def get-column [ - col_index:int # A 0 indexed col_index - ] { - - # meant to be used like `ls | get-column 1` - $in | transpose | select $col_index | transpose | select column1 | headers -} diff --git a/stdlib-candidate/std-rfc/get-row.nu b/stdlib-candidate/std-rfc/get-row.nu deleted file mode 100644 index 401ef91..0000000 --- a/stdlib-candidate/std-rfc/get-row.nu +++ /dev/null @@ -1,6 +0,0 @@ -# This is a wrapper to get a row -def get-row [ - row_num:int # A 0 indexed row - ] { - $in | get $row_num | table --index $row_num -} diff --git a/stdlib-candidate/std-rfc/mod.nu b/stdlib-candidate/std-rfc/mod.nu index 7a5e469..99468a5 100644 --- a/stdlib-candidate/std-rfc/mod.nu +++ b/stdlib-candidate/std-rfc/mod.nu @@ -1,9 +1,12 @@ -# modules +# Modules export module record/ export module str/ export module math/ -# commands + +# Commands export use bulk-rename.nu * export use set-env.nu * export use bench.nu export use script-parsing.nu [ parse-arg ] +export use conversions * +export use tables * diff --git a/stdlib-candidate/std-rfc/tables/col-indices.nu b/stdlib-candidate/std-rfc/tables/col-indices.nu new file mode 100644 index 0000000..02f9d04 --- /dev/null +++ b/stdlib-candidate/std-rfc/tables/col-indices.nu @@ -0,0 +1,15 @@ +use ../conversions/into.nu * +use ./select-ranges.nu * + +export def main [ ...ranges ] { + let indices = ( + $ranges + | reduce -f [] {|range,indices| + $indices ++ ($range | into list) + } + ) + + $in | columns + | select ranges $indices + | get item +} \ No newline at end of file diff --git a/stdlib-candidate/std-rfc/tables/mod.nu b/stdlib-candidate/std-rfc/tables/mod.nu new file mode 100644 index 0000000..2d6304e --- /dev/null +++ b/stdlib-candidate/std-rfc/tables/mod.nu @@ -0,0 +1,6 @@ +export use ./select-ranges.nu * +export use ./reject-ranges.nu * +export use ./select-column-ranges.nu * +export use ./reject-column-ranges.nu * +export use ./row-indices.nu * +export use ./col-indices.nu * \ No newline at end of file diff --git a/stdlib-candidate/std-rfc/tables/reject-column-ranges.nu b/stdlib-candidate/std-rfc/tables/reject-column-ranges.nu new file mode 100644 index 0000000..62789fb --- /dev/null +++ b/stdlib-candidate/std-rfc/tables/reject-column-ranges.nu @@ -0,0 +1,13 @@ +use ./col-indices.nu * + +# Relect a range of columns by their indices +# +# Example: +# +# ls | reject column-ranges 0 4 5 | first 3 +export def "reject column-ranges" [ + ...ranges +] { + let column_selector = ($in | col-indices ...$ranges) + $in | reject ...$column_selector +} \ No newline at end of file diff --git a/stdlib-candidate/std-rfc/tables/reject-ranges.nu b/stdlib-candidate/std-rfc/tables/reject-ranges.nu new file mode 100644 index 0000000..05e39a6 --- /dev/null +++ b/stdlib-candidate/std-rfc/tables/reject-ranges.nu @@ -0,0 +1,14 @@ +use ./row-indices.nu * + +# Rejects one or more rows while keeping +# the original indices. +# +# Example - Rejects the first, fifth, and +# sixth rows from the table: +# +# ls / | reject ranges 0 4..5 +export def "reject ranges" [ ...ranges ] { + enumerate + | flatten + | reject ...(row-indices ...$ranges) +} diff --git a/stdlib-candidate/std-rfc/tables/row-indices.nu b/stdlib-candidate/std-rfc/tables/row-indices.nu new file mode 100644 index 0000000..c153d12 --- /dev/null +++ b/stdlib-candidate/std-rfc/tables/row-indices.nu @@ -0,0 +1,25 @@ +use ../conversions/into.nu * + +# Return a list of indices +# for the provided ranges or indices. +# Primarily used as a helper for +# "select ranges" et. al. +# +# Example: +# +# row-indices 0 2..5 7..8 +# # => ╭───┬───╮ +# # => │ 0 │ 0 │ +# # => │ 1 │ 2 │ +# # => │ 2 │ 3 │ +# # => │ 3 │ 4 │ +# # => │ 4 │ 5 │ +# # => │ 5 │ 7 │ +# # => │ 6 │ 8 │ +# # => ╰───┴───╯ +export def main [ ...ranges ] { + $ranges + | reduce -f [] {|range,indices| + $indices ++ ($range | into list) + } +} \ No newline at end of file diff --git a/stdlib-candidate/std-rfc/tables/select-column-ranges.nu b/stdlib-candidate/std-rfc/tables/select-column-ranges.nu new file mode 100644 index 0000000..f4bd8ea --- /dev/null +++ b/stdlib-candidate/std-rfc/tables/select-column-ranges.nu @@ -0,0 +1,20 @@ +use ./col-indices.nu * + +# Select a range of columns by their indices +# +# Example: +# +# ls -l | select column-ranges 0 10..12 | first 3 +# # => ╭───┬────────────────────┬──────────────┬─────────────┬──────────────╮ +# # => │ # │ name │ created │ accessed │ modified │ +# # => ├───┼────────────────────┼──────────────┼─────────────┼──────────────┤ +# # => │ 0 │ CITATION.cff │ 3 months ago │ 4 hours ago │ 3 months ago │ +# # => │ 1 │ CODE_OF_CONDUCT.md │ 7 months ago │ 4 hours ago │ 7 months ago │ +# # => │ 2 │ CONTRIBUTING.md │ 3 months ago │ 4 hours ago │ 3 months ago │ +# # => ╰───┴────────────────────┴──────────────┴─────────────┴──────────────╯ +export def "select column-ranges" [ + ...ranges +] { + let column_selector = ($in | col-indices ...$ranges) + $in | select ...$column_selector +} \ No newline at end of file diff --git a/stdlib-candidate/std-rfc/tables/select-ranges.nu b/stdlib-candidate/std-rfc/tables/select-ranges.nu new file mode 100644 index 0000000..4b34b26 --- /dev/null +++ b/stdlib-candidate/std-rfc/tables/select-ranges.nu @@ -0,0 +1,26 @@ +use ./row-indices.nu * + +# Selects one or more rows while keeping +# the original indices. +# +# Example - Selects the first, fifth, and +# sixth rows from the table: +# +# ls / | select ranges 0 4..5 +# +# Example - Select the 5th row: +# +# ls / | select 5 +# +# Example - Select the 4th row. +# Note that the difference beteen this +# and `select 3` is that the index (#) +# column shows the *original* (pre-select) +# position in the table. +# +# ls | select ranges 3 +export def "select ranges" [ ...ranges ] { + enumerate + | flatten + | select ...(row-indices ...$ranges) +} diff --git a/stdlib-candidate/tests/bulk-rename.nu b/stdlib-candidate/tests/bulk-rename.nu index 7e62404..7e46aae 100644 --- a/stdlib-candidate/tests/bulk-rename.nu +++ b/stdlib-candidate/tests/bulk-rename.nu @@ -40,7 +40,10 @@ export def 'test --verbose' [] { _test.nu ] let renamed = test $fixture { - ls $in | bulk-rename --verbose --no-execute { '_' + $in } + # Note: Currently failing due to Nushell core #13267 + # Remove the 'sort' once it is fixed + # ls $in | bulk-rename --verbose --no-execute { '_' + $in } + ls $in | bulk-rename --verbose --no-execute { '_' + $in } | sort } assert equal ($renamed.new | each { path basename }) $expects } diff --git a/stdlib-candidate/tests/conversions.nu b/stdlib-candidate/tests/conversions.nu new file mode 100644 index 0000000..ba6e0d8 --- /dev/null +++ b/stdlib-candidate/tests/conversions.nu @@ -0,0 +1,43 @@ +use std assert +use ../std-rfc/conversions * + +export def "test range-into-list" [] { + assert equal ( + 1..10 | into list + ) ( + [ 1 2 3 4 5 6 7 8 9 10 ] + ) +} + +export def "test string-into-list" [] { + assert equal ( + "foo" | into list + ) ( + [ foo ] + ) +} + +export def "test range-stride-into-list" [] { + assert equal ( + 0..2..10 | into list + ) ( + [ 0 2 4 6 8 10 ] + ) +} + +export def "test null-into-list" [] { + assert equal ( + null | into list | get 0 | describe + ) ( + "nothing" + ) +} + +export def "test list-into-list" [] { + assert equal ( + [ foo bar baz ] | into list + ) ( + [ foo bar baz ] + ) + +} diff --git a/stdlib-candidate/tests/mod.nu b/stdlib-candidate/tests/mod.nu index 8fdde90..ce56aa8 100644 --- a/stdlib-candidate/tests/mod.nu +++ b/stdlib-candidate/tests/mod.nu @@ -5,3 +5,5 @@ export module math.nu export module bench.nu export module script-parsing.nu export module str_dedent.nu +export module conversions.nu +export module tables.nu \ No newline at end of file diff --git a/stdlib-candidate/tests/tables.nu b/stdlib-candidate/tests/tables.nu new file mode 100644 index 0000000..4d22d5b --- /dev/null +++ b/stdlib-candidate/tests/tables.nu @@ -0,0 +1,168 @@ +use std assert +use ../std-rfc/tables * + +const test_table = [ + [ col-a col-b col-c col-d col-e col-f ]; + [ 'a0' 'b0' 'c0' 'd0' 'e0' 'f0' ] + [ 'a1' 'b1' 'c1' 'd1' 'e1' 'f1' ] + [ 'a2' 'b2' 'c2' 'd2' 'e2' 'f2' ] + [ 'a3' 'b3' 'c3' 'd3' 'e3' 'f3' ] + [ 'a4' 'b4' 'c4' 'd4' 'e4' 'f4' ] + [ 'a5' 'b5' 'c5' 'd5' 'e5' 'f5' ] + [ 'a6' 'b6' 'c6' 'd6' 'e6' 'f6' ] + [ 'a7' 'b7' 'c7' 'd7' 'e7' 'f7' ] + [ 'a8' 'b8' 'c8' 'd8' 'e8' 'f8' ] + [ 'a9' 'b9' 'c9' 'd9' 'e9' 'f9' ] +] + +const enumerated_table = [ + [ index col-a col-b col-c col-d col-e col-f ]; + [ 0 'a0' 'b0' 'c0' 'd0' 'e0' 'f0' ] + [ 1 'a1' 'b1' 'c1' 'd1' 'e1' 'f1' ] + [ 2 'a2' 'b2' 'c2' 'd2' 'e2' 'f2' ] + [ 3 'a3' 'b3' 'c3' 'd3' 'e3' 'f3' ] + [ 4 'a4' 'b4' 'c4' 'd4' 'e4' 'f4' ] + [ 5 'a5' 'b5' 'c5' 'd5' 'e5' 'f5' ] + [ 6 'a6' 'b6' 'c6' 'd6' 'e6' 'f6' ] + [ 7 'a7' 'b7' 'c7' 'd7' 'e7' 'f7' ] + [ 8 'a8' 'b8' 'c8' 'd8' 'e8' 'f8' ] + [ 9 'a9' 'b9' 'c9' 'd9' 'e9' 'f9' ] +] + +export def "test row-indices-range" [] { + assert equal ( + row-indices 0..3 10..11 + ) ( + [ 0 1 2 3 10 11 ] + ) +} + +export def "test row-indices-index" [] { + assert equal ( + row-indices 4 + ) ( + [ 4 ] + ) +} + +export def "test row-indices-complex" [] { + assert equal ( + row-indices 0..2..6 3 7 + ) ( + [ 0 2 4 6 3 7 ] + ) +} + +export def "test col-index-ints" [] { + assert equal ( + # Third and Fifth Columns + $test_table | col-indices 2 4 + ) ( + [ 'col-c', 'col-e' ] + ) +} + +export def "test col-index-complex" [] { + assert equal ( + # Every other column, plus the second + $test_table | col-indices 0..2..10 1 + ) ( + [ 'col-a', 'col-b', 'col-c', 'col-e' ] + ) +} + +export def "test select-range single-int" [] { + assert equal ( + $test_table | select ranges 1 + ) ( + $enumerated_table | select 1 + ) +} + +export def "test select-range single-range" [] { + assert equal ( + $test_table | select ranges 2..4 + ) ( + $enumerated_table | select 2 3 4 + ) +} + +export def "test select-range complex" [] { + assert equal ( + # First and every following third-row + second row + $test_table | select ranges 1 0..3..100 + ) ( + $enumerated_table | select 0 1 3 6 9 + ) +} + +export def "test select-range out-of-bounds" [] { + assert equal ( + $test_table | select ranges 100 + ) ( + [] + ) +} + +export def "test reject-range single-index" [] { + assert equal ( + $test_table | reject ranges 4 + ) ( + $enumerated_table | reject 4 + ) +} + +export def "test reject-range ranges" [] { + assert equal ( + # Reject rows 0-3 and 5-9, leaving only 4 + $test_table | reject ranges 0..3 5..9 + ) ( + $enumerated_table | select 4 + ) +} + +export def "test reject-range out-of-bounds" [] { + assert error { + $test_table | reject ranges 1000 + } +} + +export def "test select-col index" [] { + assert equal ( + $test_table | select column-ranges 2 + ) ( + $test_table | select col-c + ) +} + +export def "test select-col indices" [] { + assert equal ( + $test_table | select column-ranges 2 4 + ) ( + $test_table | select col-c col-e + ) +} + +export def "test select-col ranges-and-index" [] { + assert equal ( + $test_table | select column-ranges 0..2..5 1 + ) ( + $test_table | select col-a col-c col-e col-b + ) +} + +export def "test reject-col ranges-and-index" [] { + assert equal ( + $test_table | reject column-ranges 0..2..5 1 + ) ( + $enumerated_table | select col-d col-f + ) +} + +export def "test reject-col out-of-bounds" [] { + assert equal ( + $test_table | reject column-ranges 1_000 + ) ( + $test_table + ) +}