Allow dropping columns. (#3107)

`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
─────────
```
This commit is contained in:
Andrés N. Robalino 2021-02-25 14:37:21 -06:00 committed by GitHub
parent 84169a91ff
commit 19d5f782cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 153 additions and 29 deletions

View File

@ -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;

View File

@ -114,6 +114,7 @@ pub fn create_default_context(interactive: bool) -> Result<EvaluationContext, Bo
whole_stream_command(AnsiStrip),
whole_stream_command(Char),
// Column manipulation
whole_stream_command(DropColumn),
whole_stream_command(Move),
whole_stream_command(Reject),
whole_stream_command(Select),

View File

@ -0,0 +1,86 @@
use crate::prelude::*;
use nu_data::base::select_fields;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape};
use nu_source::Tagged;
pub struct SubCommand;
#[derive(Deserialize)]
pub struct Arguments {
columns: Option<Tagged<u64>>,
}
#[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<OutputStream, ShellError> {
drop(args).await
}
fn examples(&self) -> Vec<Example> {
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<OutputStream, ShellError> {
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 {})
}
}

View File

@ -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<Tagged<u64>>,
}
#[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<OutputStream, ShellError> {
@ -53,7 +53,7 @@ impl WholeStreamCommand for Drop {
}
async fn drop(args: CommandArgs) -> Result<OutputStream, ShellError> {
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<OutputStream, ShellError> {
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 {})
}
}

View File

@ -0,0 +1,5 @@
mod column;
mod command;
pub use column::SubCommand as DropColumn;
pub use command::Command as Drop;

View File

@ -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");
}