mirror of
https://github.com/nushell/nushell
synced 2024-12-27 05:23:11 +00:00
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:
parent
eb1ada6115
commit
3d63901b3b
6 changed files with 400 additions and 0 deletions
|
@ -320,6 +320,7 @@ pub fn create_default_context(
|
|||
whole_stream_command(GroupByDate),
|
||||
whole_stream_command(First),
|
||||
whole_stream_command(Last),
|
||||
whole_stream_command(Every),
|
||||
whole_stream_command(Nth),
|
||||
whole_stream_command(Drop),
|
||||
whole_stream_command(Format),
|
||||
|
|
|
@ -30,6 +30,7 @@ pub(crate) mod echo;
|
|||
pub(crate) mod enter;
|
||||
#[allow(unused)]
|
||||
pub(crate) mod evaluate_by;
|
||||
pub(crate) mod every;
|
||||
pub(crate) mod exit;
|
||||
pub(crate) mod first;
|
||||
pub(crate) mod format;
|
||||
|
@ -160,6 +161,7 @@ pub(crate) mod touch;
|
|||
pub(crate) use enter::Enter;
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use evaluate_by::EvaluateBy;
|
||||
pub(crate) use every::Every;
|
||||
pub(crate) use exit::Exit;
|
||||
pub(crate) use first::First;
|
||||
pub(crate) use format::Format;
|
||||
|
|
105
crates/nu-cli/src/commands/every.rs
Normal file
105
crates/nu-cli/src/commands/every.rs
Normal 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(®istry).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 {})
|
||||
}
|
||||
}
|
245
crates/nu-cli/tests/commands/every.rs
Normal file
245
crates/nu-cli/tests/commands/every.rs
Normal 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);
|
||||
})
|
||||
}
|
|
@ -10,6 +10,7 @@ mod default;
|
|||
mod drop;
|
||||
mod each;
|
||||
mod enter;
|
||||
mod every;
|
||||
mod first;
|
||||
mod format;
|
||||
mod get;
|
||||
|
|
46
docs/commands/every.md
Normal file
46
docs/commands/every.md
Normal 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
|
||||
───┴─────────┴──────┴─────────────────
|
||||
```
|
Loading…
Reference in a new issue