Add 'every' command to select (or skip) every nth row (#1992)

* Add 'every' command

* Add --skip option to 'every' command

This option skips instead of selects every nth row

* Fix descriptions for 'every' command

* Add docummentation for 'every' command

* Check actual filenames in 'every' command tests
This commit is contained in:
Jakub Žádník 2020-06-16 22:58:41 +03:00 committed by GitHub
parent eb1ada6115
commit 3d63901b3b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 400 additions and 0 deletions

View File

@ -320,6 +320,7 @@ pub fn create_default_context(
whole_stream_command(GroupByDate), whole_stream_command(GroupByDate),
whole_stream_command(First), whole_stream_command(First),
whole_stream_command(Last), whole_stream_command(Last),
whole_stream_command(Every),
whole_stream_command(Nth), whole_stream_command(Nth),
whole_stream_command(Drop), whole_stream_command(Drop),
whole_stream_command(Format), whole_stream_command(Format),

View File

@ -30,6 +30,7 @@ pub(crate) mod echo;
pub(crate) mod enter; pub(crate) mod enter;
#[allow(unused)] #[allow(unused)]
pub(crate) mod evaluate_by; pub(crate) mod evaluate_by;
pub(crate) mod every;
pub(crate) mod exit; pub(crate) mod exit;
pub(crate) mod first; pub(crate) mod first;
pub(crate) mod format; pub(crate) mod format;
@ -160,6 +161,7 @@ pub(crate) mod touch;
pub(crate) use enter::Enter; pub(crate) use enter::Enter;
#[allow(unused_imports)] #[allow(unused_imports)]
pub(crate) use evaluate_by::EvaluateBy; pub(crate) use evaluate_by::EvaluateBy;
pub(crate) use every::Every;
pub(crate) use exit::Exit; pub(crate) use exit::Exit;
pub(crate) use first::First; pub(crate) use first::First;
pub(crate) use format::Format; pub(crate) use format::Format;

View File

@ -0,0 +1,105 @@
use crate::commands::WholeStreamCommand;
use crate::context::CommandRegistry;
use crate::prelude::*;
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue};
use nu_source::Tagged;
pub struct Every;
#[derive(Deserialize)]
pub struct EveryArgs {
stride: Tagged<u64>,
skip: Tagged<bool>,
}
#[async_trait]
impl WholeStreamCommand for Every {
fn name(&self) -> &str {
"every"
}
fn signature(&self) -> Signature {
Signature::build(self.name())
.required(
"stride",
SyntaxShape::Int,
"how many rows to skip between (and including) each row returned",
)
.switch(
"skip",
"skip the rows that would be returned, instead of selecting them",
Some('s'),
)
}
fn usage(&self) -> &str {
"Show (or skip) every n-th row, starting from the first one."
}
async fn run(
&self,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
every(args, registry).await
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Get every second row",
example: "echo [1 2 3 4 5] | every 2",
result: Some(vec![
UntaggedValue::int(1).into(),
UntaggedValue::int(3).into(),
UntaggedValue::int(5).into(),
]),
},
Example {
description: "Skip every second row",
example: "echo [1 2 3 4 5] | every 2 --skip",
result: Some(vec![
UntaggedValue::int(2).into(),
UntaggedValue::int(4).into(),
]),
},
]
}
}
async fn every(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
let registry = registry.clone();
let (EveryArgs { stride, skip }, input) = args.process(&registry).await?;
let v: Vec<_> = input.into_vec().await;
let stride_desired = if stride.item < 1 { 1 } else { stride.item } as usize;
let mut values_vec_deque = VecDeque::new();
for (i, x) in v.iter().enumerate() {
let should_include = if skip.item {
i % stride_desired != 0
} else {
i % stride_desired == 0
};
if should_include {
values_vec_deque.push_back(ReturnSuccess::value(x.clone()));
}
}
Ok(futures::stream::iter(values_vec_deque).to_output_stream())
}
#[cfg(test)]
mod tests {
use super::Every;
#[test]
fn examples_work_as_expected() {
use crate::examples::test as test_examples;
test_examples(Every {})
}
}

View File

@ -0,0 +1,245 @@
use nu_test_support::fs::Stub::EmptyFile;
use nu_test_support::playground::Playground;
use nu_test_support::{nu, pipeline};
#[test]
fn gets_all_rows_by_every_zero() {
Playground::setup("every_test_1", |dirs, sandbox| {
sandbox.with_files(vec![
EmptyFile("los.txt"),
EmptyFile("tres.txt"),
EmptyFile("amigos.txt"),
EmptyFile("arepas.clu"),
]);
let actual = nu!(
cwd: dirs.test(), pipeline(
r#"
ls
| get name
| every 0
"#
));
let expected = nu!(
cwd: dirs.test(), pipeline(
r#"
echo [ amigos.txt arepas.clu los.txt tres.txt ]
"#
));
assert_eq!(actual.out, expected.out);
})
}
#[test]
fn gets_no_rows_by_every_skip_zero() {
Playground::setup("every_test_2", |dirs, sandbox| {
sandbox.with_files(vec![
EmptyFile("los.txt"),
EmptyFile("tres.txt"),
EmptyFile("amigos.txt"),
EmptyFile("arepas.clu"),
]);
let actual = nu!(
cwd: dirs.test(), pipeline(
r#"
ls
| get name
| every 0 --skip
"#
));
let expected = nu!(
cwd: dirs.test(), pipeline(
r#"
echo [ ]
"#
));
assert_eq!(actual.out, expected.out);
})
}
#[test]
fn gets_all_rows_by_every_one() {
Playground::setup("every_test_3", |dirs, sandbox| {
sandbox.with_files(vec![
EmptyFile("los.txt"),
EmptyFile("tres.txt"),
EmptyFile("amigos.txt"),
EmptyFile("arepas.clu"),
]);
let actual = nu!(
cwd: dirs.test(), pipeline(
r#"
ls
| get name
| every 1
"#
));
let expected = nu!(
cwd: dirs.test(), pipeline(
r#"
echo [ amigos.txt arepas.clu los.txt tres.txt ]
"#
));
assert_eq!(actual.out, expected.out);
})
}
#[test]
fn gets_no_rows_by_every_skip_one() {
Playground::setup("every_test_4", |dirs, sandbox| {
sandbox.with_files(vec![
EmptyFile("los.txt"),
EmptyFile("tres.txt"),
EmptyFile("amigos.txt"),
EmptyFile("arepas.clu"),
]);
let actual = nu!(
cwd: dirs.test(), pipeline(
r#"
ls
| get name
| every 1 --skip
"#
));
let expected = nu!(
cwd: dirs.test(), pipeline(
r#"
echo [ ]
"#
));
assert_eq!(actual.out, expected.out);
})
}
#[test]
fn gets_first_row_by_every_too_much() {
Playground::setup("every_test_5", |dirs, sandbox| {
sandbox.with_files(vec![
EmptyFile("los.txt"),
EmptyFile("tres.txt"),
EmptyFile("amigos.txt"),
EmptyFile("arepas.clu"),
]);
let actual = nu!(
cwd: dirs.test(), pipeline(
r#"
ls
| get name
| every 999
"#
));
let expected = nu!(
cwd: dirs.test(), pipeline(
r#"
echo [ amigos.txt ]
"#
));
assert_eq!(actual.out, expected.out);
})
}
#[test]
fn gets_all_rows_except_first_by_every_skip_too_much() {
Playground::setup("every_test_6", |dirs, sandbox| {
sandbox.with_files(vec![
EmptyFile("los.txt"),
EmptyFile("tres.txt"),
EmptyFile("amigos.txt"),
EmptyFile("arepas.clu"),
]);
let actual = nu!(
cwd: dirs.test(), pipeline(
r#"
ls
| get name
| every 999 --skip
"#
));
let expected = nu!(
cwd: dirs.test(), pipeline(
r#"
echo [ arepas.clu los.txt tres.txt ]
"#
));
assert_eq!(actual.out, expected.out);
})
}
#[test]
fn gets_every_third_row() {
Playground::setup("every_test_7", |dirs, sandbox| {
sandbox.with_files(vec![
EmptyFile("los.txt"),
EmptyFile("tres.txt"),
EmptyFile("quatro.txt"),
EmptyFile("amigos.txt"),
EmptyFile("arepas.clu"),
]);
let actual = nu!(
cwd: dirs.test(), pipeline(
r#"
ls
| get name
| every 3
"#
));
let expected = nu!(
cwd: dirs.test(), pipeline(
r#"
echo [ amigos.txt quatro.txt ]
"#
));
assert_eq!(actual.out, expected.out);
})
}
#[test]
fn skips_every_third_row() {
Playground::setup("every_test_8", |dirs, sandbox| {
sandbox.with_files(vec![
EmptyFile("los.txt"),
EmptyFile("tres.txt"),
EmptyFile("quatro.txt"),
EmptyFile("amigos.txt"),
EmptyFile("arepas.clu"),
]);
let actual = nu!(
cwd: dirs.test(), pipeline(
r#"
ls
| get name
| every 3 --skip
"#
));
let expected = nu!(
cwd: dirs.test(), pipeline(
r#"
echo [ arepas.clu los.txt tres.txt ]
"#
));
assert_eq!(actual.out, expected.out);
})
}

View File

@ -10,6 +10,7 @@ mod default;
mod drop; mod drop;
mod each; mod each;
mod enter; mod enter;
mod every;
mod first; mod first;
mod format; mod format;
mod get; mod get;

46
docs/commands/every.md Normal file
View File

@ -0,0 +1,46 @@
# every
Selects every n-th row of a table, starting from the first one. With the `--skip` flag, every n-th row will be skipped, inverting the original functionality.
Syntax: `> [input-command] | every <stride> {flags}`
## Flags
* `--skip`, `-s`: Skip the rows that would be returned, instead of selecting them
## Examples
```shell
> open contacts.csv
───┬─────────┬──────┬─────────────────
# │ first │ last │ email
───┼─────────┼──────┼─────────────────
0 │ John │ Doe │ doe.1@email.com
1 │ Jane │ Doe │ doe.2@email.com
2 │ Chris │ Doe │ doe.3@email.com
3 │ Francis │ Doe │ doe.4@email.com
4 │ Stella │ Doe │ doe.5@email.com
───┴─────────┴──────┴─────────────────
```
```shell
> open contacts.csv | every 2
───┬─────────┬──────┬─────────────────
# │ first │ last │ email
───┼─────────┼──────┼─────────────────
0 │ John │ Doe │ doe.1@email.com
2 │ Chris │ Doe │ doe.3@email.com
4 │ Stella │ Doe │ doe.5@email.com
───┴─────────┴──────┴─────────────────
```
```shell
> open contacts.csv | every 2 --skip
───┬─────────┬──────┬─────────────────
# │ first │ last │ email
───┼─────────┼──────┼─────────────────
1 │ Jane │ Doe │ doe.2@email.com
3 │ Francis │ Doe │ doe.4@email.com
───┴─────────┴──────┴─────────────────
```