mirror of
https://github.com/nushell/nushell.git
synced 2024-11-22 08:23:24 +01: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
@ -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),
|
||||||
|
@ -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;
|
||||||
|
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 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
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 New Issue
Block a user