From 19d5f782ccf2725d9a8335cfb39dbaba7fa2314f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20N=2E=20Robalino?= Date: Thu, 25 Feb 2021 14:37:21 -0600 Subject: [PATCH] Allow dropping columns. (#3107) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `drop` is used for removing the last row. Passing a number allows dropping N rows. Here we introduce the same logic for dropping columns instead. You can certainly remove columns by using `reject`, however, there could be cases where we are interested in removing columns from tables that contain, say, a big number of columns. Using `reject` becomes impractical, especially when you don't care about the column names that could either be known or not known when exploring tables. ``` > echo [[lib, extension]; [nu-core, rs] [rake, rb]] ─────────┬─────────── lib │ extension ─────────┼─────────── nu-core │ rs rake │ rb ─────────┴─────────── ``` ``` > echo [[lib, extension]; [nu-core, rs] [rake, rb]] | drop column ───────── lib ───────── nu-core rake ───────── ``` --- crates/nu-command/src/commands.rs | 2 +- .../src/commands/default_context.rs | 1 + crates/nu-command/src/commands/drop/column.rs | 86 +++++++++++++++++++ .../src/commands/{drop.rs => drop/command.rs} | 23 ++--- crates/nu-command/src/commands/drop/mod.rs | 5 ++ crates/nu-command/tests/commands/drop.rs | 65 +++++++++++--- 6 files changed, 153 insertions(+), 29 deletions(-) create mode 100644 crates/nu-command/src/commands/drop/column.rs rename crates/nu-command/src/commands/{drop.rs => drop/command.rs} (82%) create mode 100644 crates/nu-command/src/commands/drop/mod.rs diff --git a/crates/nu-command/src/commands.rs b/crates/nu-command/src/commands.rs index e55e1be027..972d5d0875 100644 --- a/crates/nu-command/src/commands.rs +++ b/crates/nu-command/src/commands.rs @@ -163,7 +163,7 @@ pub(crate) use def::Def; pub(crate) use default::Default; pub(crate) use describe::Describe; pub(crate) use do_::Do; -pub(crate) use drop::Drop; +pub(crate) use drop::{Drop, DropColumn}; pub(crate) use du::Du; pub(crate) use each::Each; pub(crate) use each::EachGroup; diff --git a/crates/nu-command/src/commands/default_context.rs b/crates/nu-command/src/commands/default_context.rs index 045defc02a..e85429511f 100644 --- a/crates/nu-command/src/commands/default_context.rs +++ b/crates/nu-command/src/commands/default_context.rs @@ -114,6 +114,7 @@ pub fn create_default_context(interactive: bool) -> Result>, +} + +#[async_trait] +impl WholeStreamCommand for SubCommand { + fn name(&self) -> &str { + "drop column" + } + + fn signature(&self) -> Signature { + Signature::build("drop column").optional( + "columns", + SyntaxShape::Number, + "starting from the end, the number of columns to remove", + ) + } + + fn usage(&self) -> &str { + "Remove the last number of columns. If you want to remove columns by name, try 'reject'." + } + + async fn run(&self, args: CommandArgs) -> Result { + drop(args).await + } + + fn examples(&self) -> Vec { + use nu_protocol::{row, Value}; + + vec![Example { + description: "Remove the last column of a table", + example: "echo [[lib, extension]; [nu-lib, rs] [nu-core, rb]] | drop column", + result: Some(vec![ + row! { "lib".into() => Value::from("nu-lib") }, + row! { "lib".into() => Value::from("nu-core") }, + ]), + }] + } +} + +async fn drop(args: CommandArgs) -> Result { + let (Arguments { columns }, input) = args.process().await?; + + let to_drop = if let Some(quantity) = columns { + *quantity as usize + } else { + 1 + }; + + Ok(input + .map(move |item| { + let headers = item.data_descriptors(); + + let descs = match headers.len() { + 0 => &headers[..], + n if to_drop > n => &[], + n => &headers[..n - to_drop], + }; + + select_fields(&item, descs, item.tag()) + }) + .map(ReturnSuccess::value) + .to_output_stream()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn examples_work_as_expected() -> Result<(), ShellError> { + use crate::examples::test as test_examples; + + test_examples(SubCommand {}) + } +} diff --git a/crates/nu-command/src/commands/drop.rs b/crates/nu-command/src/commands/drop/command.rs similarity index 82% rename from crates/nu-command/src/commands/drop.rs rename to crates/nu-command/src/commands/drop/command.rs index 684b3e268c..289899a74f 100644 --- a/crates/nu-command/src/commands/drop.rs +++ b/crates/nu-command/src/commands/drop/command.rs @@ -4,15 +4,15 @@ use nu_errors::ShellError; use nu_protocol::{Signature, SyntaxShape, UntaggedValue}; use nu_source::Tagged; -pub struct Drop; +pub struct Command; #[derive(Deserialize)] -pub struct DropArgs { +pub struct Arguments { rows: Option>, } #[async_trait] -impl WholeStreamCommand for Drop { +impl WholeStreamCommand for Command { fn name(&self) -> &str { "drop" } @@ -26,7 +26,7 @@ impl WholeStreamCommand for Drop { } fn usage(&self) -> &str { - "Remove the last number of rows. If you want to remove columns, try 'reject'." + "Remove the last number of rows. If you want to remove columns, try 'drop column'." } async fn run(&self, args: CommandArgs) -> Result { @@ -53,7 +53,7 @@ impl WholeStreamCommand for Drop { } async fn drop(args: CommandArgs) -> Result { - let (DropArgs { rows }, input) = args.process().await?; + let (Arguments { rows }, input) = args.process().await?; let v: Vec<_> = input.into_vec().await; let rows_to_drop = if let Some(quantity) = rows { @@ -76,16 +76,3 @@ async fn drop(args: CommandArgs) -> Result { futures::stream::iter(iter).to_output_stream() }) } - -#[cfg(test)] -mod tests { - use super::Drop; - use super::ShellError; - - #[test] - fn examples_work_as_expected() -> Result<(), ShellError> { - use crate::examples::test as test_examples; - - test_examples(Drop {}) - } -} diff --git a/crates/nu-command/src/commands/drop/mod.rs b/crates/nu-command/src/commands/drop/mod.rs new file mode 100644 index 0000000000..428affff8f --- /dev/null +++ b/crates/nu-command/src/commands/drop/mod.rs @@ -0,0 +1,5 @@ +mod column; +mod command; + +pub use column::SubCommand as DropColumn; +pub use command::Command as Drop; diff --git a/crates/nu-command/tests/commands/drop.rs b/crates/nu-command/tests/commands/drop.rs index ec91f7efb6..1ce67368e9 100644 --- a/crates/nu-command/tests/commands/drop.rs +++ b/crates/nu-command/tests/commands/drop.rs @@ -1,23 +1,68 @@ use nu_test_support::{nu, pipeline}; #[test] -fn drop_rows() { +fn columns() { let actual = nu!( - cwd: "tests/fixtures/formats", - r#"echo '[{"foo": 3}, {"foo": 8}, {"foo": 4}]' | from json | drop 2 | get foo | math sum "# + cwd: ".", pipeline(r#" + echo [ + [arepas, color]; + + [3, white] + [8, yellow] + [4, white] + ] + | drop column + | get + | count + "#) + ); + + assert_eq!(actual.out, "1"); +} + +#[test] +fn more_columns_than_table_has() { + let actual = nu!( + cwd: ".", pipeline(r#" + echo [ + [arepas, color]; + + [3, white] + [8, yellow] + [4, white] + ] + | drop column 3 + | get + | empty? + "#) + ); + + assert_eq!(actual.out, "true"); +} + +#[test] +fn rows() { + let actual = nu!( + cwd: ".", pipeline(r#" + echo [ + [arepas]; + + [3] + [8] + [4] + ] + | drop 2 + | get arepas + | math sum + "#) ); assert_eq!(actual.out, "3"); } #[test] -fn drop_more_rows_than_table_has() { - let actual = nu!( - cwd: ".", pipeline( - r#" - date | drop 50 | count - "# - )); +fn more_rows_than_table_has() { + let actual = nu!(cwd: ".", "date | drop 50 | count"); assert_eq!(actual.out, "0"); }