Remove deprecated --numbered flag from four commands (#7777)

# Description

Remove `--numbered` from ~~`for`~~, `each`, `par-each`, `reduce` and
`each while`. These all provide indexes (numbering) via the optional
second param to their closures.

EDIT: Closes #6986.

# User-Facing Changes

Every command that had `--numbered` listed as "deprecated" in their help
docs is affected.

# Tests + Formatting

Don't forget to add tests that cover your changes.

Make sure you've run and fixed any issues with these commands:

- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A
clippy::needless_collect` to check that you're using the standard code
style
- `cargo test --workspace` to check that all tests pass

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.

---------

Co-authored-by: Darren Schroeder <343840+fdncred@users.noreply.github.com>
This commit is contained in:
Leon 2023-02-03 08:59:58 +10:00 committed by GitHub
parent 215ed141e7
commit 9945241b77
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 202 additions and 541 deletions

View File

@ -34,7 +34,7 @@ impl Command for For {
.required("block", SyntaxShape::Block, "the block to run")
.switch(
"numbered",
"returned a numbered item ($it.index and $it.item)",
"return a numbered item ($it.index and $it.item)",
Some('n'),
)
.creates_scope()

View File

@ -9,9 +9,9 @@ pub fn test_examples(cmd: impl Command + 'static) {
#[cfg(test)]
mod test_examples {
use super::super::{
Ansi, Date, Echo, From, If, Into, IntoString, Let, LetEnv, Math, MathEuler, MathPi,
MathRound, Path, Random, Split, SplitColumn, SplitRow, Str, StrJoin, StrLength, StrReplace,
Url, Values, Wrap,
Ansi, Date, Echo, Enumerate, Flatten, From, Get, If, Into, IntoString, Let, LetEnv, Math,
MathEuler, MathPi, MathRound, ParEach, Path, Random, Sort, SortBy, Split, SplitColumn,
SplitRow, Str, StrJoin, StrLength, StrReplace, Update, Url, Values, Wrap,
};
use crate::{Break, Each, Mut, To};
use itertools::Itertools;
@ -61,35 +61,42 @@ mod test_examples {
// Base functions that are needed for testing
// Try to keep this working set small to keep tests running as fast as possible
let mut working_set = StateWorkingSet::new(&engine_state);
working_set.add_decl(Box::new(Ansi));
working_set.add_decl(Box::new(Break));
working_set.add_decl(Box::new(Date));
working_set.add_decl(Box::new(Each));
working_set.add_decl(Box::new(Echo));
working_set.add_decl(Box::new(Enumerate));
working_set.add_decl(Box::new(Flatten));
working_set.add_decl(Box::new(From));
working_set.add_decl(Box::new(Get));
working_set.add_decl(Box::new(If));
working_set.add_decl(Box::new(Into));
working_set.add_decl(Box::new(IntoString));
working_set.add_decl(Box::new(Let));
working_set.add_decl(Box::new(LetEnv));
working_set.add_decl(Box::new(Math));
working_set.add_decl(Box::new(MathEuler));
working_set.add_decl(Box::new(MathPi));
working_set.add_decl(Box::new(MathRound));
working_set.add_decl(Box::new(Mut));
working_set.add_decl(Box::new(Path));
working_set.add_decl(Box::new(ParEach));
working_set.add_decl(Box::new(Random));
working_set.add_decl(Box::new(Sort));
working_set.add_decl(Box::new(SortBy));
working_set.add_decl(Box::new(Split));
working_set.add_decl(Box::new(SplitColumn));
working_set.add_decl(Box::new(SplitRow));
working_set.add_decl(Box::new(Str));
working_set.add_decl(Box::new(StrJoin));
working_set.add_decl(Box::new(StrLength));
working_set.add_decl(Box::new(StrReplace));
working_set.add_decl(Box::new(From));
working_set.add_decl(Box::new(If));
working_set.add_decl(Box::new(To));
working_set.add_decl(Box::new(Into));
working_set.add_decl(Box::new(IntoString));
working_set.add_decl(Box::new(Random));
working_set.add_decl(Box::new(Split));
working_set.add_decl(Box::new(SplitColumn));
working_set.add_decl(Box::new(SplitRow));
working_set.add_decl(Box::new(Math));
working_set.add_decl(Box::new(Path));
working_set.add_decl(Box::new(Date));
working_set.add_decl(Box::new(Url));
working_set.add_decl(Box::new(Update));
working_set.add_decl(Box::new(Values));
working_set.add_decl(Box::new(Ansi));
working_set.add_decl(Box::new(Wrap));
working_set.add_decl(Box::new(LetEnv));
working_set.add_decl(Box::new(Echo));
working_set.add_decl(Box::new(Break));
working_set.add_decl(Box::new(Mut));
working_set.add_decl(Box::new(MathEuler));
working_set.add_decl(Box::new(MathPi));
working_set.add_decl(Box::new(MathRound));
// Adding the command that is being tested to the working set
working_set.add_decl(cmd);

View File

@ -43,9 +43,14 @@ impl Command for All {
example: "[[status]; [UP] [UP]] | all {|el| $el.status == UP }",
result: Some(Value::test_bool(true)),
},
Example {
description: "Check that each item is a string",
example: "[foo bar 2 baz] | all { ($in | describe) == 'string' }",
result: Some(Value::test_bool(false)),
},
Example {
description: "Check that all values are equal to twice their index",
example: "[0 2 4 6] | all {|el ind| $el == $ind * 2 }",
example: "[0 2 4 6] | enumerate | all {|i| $i.item == $i.index * 2 }",
result: Some(Value::test_bool(true)),
},
Example {
@ -80,7 +85,7 @@ impl Command for All {
let ctrlc = engine_state.ctrlc.clone();
let engine_state = engine_state.clone();
for (idx, value) in input.into_interruptible_iter(ctrlc).enumerate() {
for value in input.into_interruptible_iter(ctrlc) {
// with_env() is used here to ensure that each iteration uses
// a different set of environment variables.
// Hence, a 'cd' in the first loop won't affect the next loop.
@ -89,18 +94,6 @@ impl Command for All {
if let Some(var_id) = var_id {
stack.add_var(var_id, value.clone());
}
// Optional index argument
if let Some(var) = block.signature.get_positional(1) {
if let Some(var_id) = &var.var_id {
stack.add_var(
*var_id,
Value::Int {
val: idx as i64,
span,
},
);
}
}
let eval = eval_block(
&engine_state,

View File

@ -43,9 +43,14 @@ impl Command for Any {
example: "[[status]; [UP] [DOWN] [UP]] | any {|el| $el.status == DOWN }",
result: Some(Value::test_bool(true)),
},
Example {
description: "Check that any item is a string",
example: "[1 2 3 4] | any { ($in | describe) == 'string' }",
result: Some(Value::test_bool(false)),
},
Example {
description: "Check if any value is equal to twice its own index",
example: "[9 8 7 6] | any {|el ind| $el == $ind * 2 }",
example: "[9 8 7 6] | enumerate | any {|i| $i.item == $i.index * 2 }",
result: Some(Value::test_bool(true)),
},
Example {
@ -80,7 +85,7 @@ impl Command for Any {
let ctrlc = engine_state.ctrlc.clone();
let engine_state = engine_state.clone();
for (idx, value) in input.into_interruptible_iter(ctrlc).enumerate() {
for value in input.into_interruptible_iter(ctrlc) {
// with_env() is used here to ensure that each iteration uses
// a different set of environment variables.
// Hence, a 'cd' in the first loop won't affect the next loop.
@ -89,18 +94,6 @@ impl Command for Any {
if let Some(var_id) = var_id {
stack.add_var(var_id, value.clone());
}
// Optional index argument
if let Some(var) = block.signature.get_positional(1) {
if let Some(var_id) = &var.var_id {
stack.add_var(
*var_id,
Value::Int {
val: idx as i64,
span,
},
);
}
}
let eval = eval_block(
&engine_state,

View File

@ -35,21 +35,19 @@ with 'transpose' first."#
fn signature(&self) -> nu_protocol::Signature {
Signature::build("each")
.input_output_types(vec![(
Type::List(Box::new(Type::Any)),
Type::List(Box::new(Type::Any)),
)])
.input_output_types(vec![
(
Type::List(Box::new(Type::Any)),
Type::List(Box::new(Type::Any)),
),
(Type::Table(vec![]), Type::List(Box::new(Type::Any))),
])
.required(
"closure",
SyntaxShape::Closure(Some(vec![SyntaxShape::Any, SyntaxShape::Int])),
"the closure to run",
)
.switch("keep-empty", "keep empty result cells", Some('k'))
.switch(
"numbered",
"iterate with an index (deprecated; use a two-parameter closure instead)",
Some('n'),
)
.category(Category::Filters)
}
@ -96,7 +94,7 @@ with 'transpose' first."#
}),
},
Example {
example: r#"[1 2 3] | each {|el ind| if $el == 2 { $"found 2 at ($ind)!"} }"#,
example: r#"[1 2 3] | enumerate | each {|e| if $e.item == 2 { $"found 2 at ($e.index)!"} }"#,
description:
"Iterate over each element, producing a list showing indexes of any 2s",
result: Some(Value::List {
@ -106,7 +104,7 @@ with 'transpose' first."#
},
Example {
example: r#"[1 2 3] | each --keep-empty {|e| if $e == 2 { "found 2!"} }"#,
description: "Iterate over each element, keeping all results",
description: "Iterate over each element, keeping null results",
result: Some(Value::List {
vals: stream_test_2,
span: Span::test_data(),
@ -124,7 +122,6 @@ with 'transpose' first."#
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
let capture_block: Closure = call.req(engine_state, stack, 0)?;
let numbered = call.has_flag("numbered");
let keep_empty = call.has_flag("keep-empty");
let metadata = input.metadata();
@ -144,11 +141,8 @@ with 'transpose' first."#
PipelineData::Value(Value::Range { .. }, ..)
| PipelineData::Value(Value::List { .. }, ..)
| PipelineData::ListStream { .. } => Ok(input
// To enumerate over the input (for the index argument),
// it must be converted into an iterator using into_iter().
.into_iter()
.enumerate()
.map_while(move |(idx, x)| {
.map_while(move |x| {
// with_env() is used here to ensure that each iteration uses
// a different set of environment variables.
// Hence, a 'cd' in the first loop won't affect the next loop.
@ -156,37 +150,7 @@ with 'transpose' first."#
if let Some(var) = block.signature.get_positional(0) {
if let Some(var_id) = &var.var_id {
// -n changes the first argument into an {index, item} record.
if numbered {
stack.add_var(
*var_id,
Value::Record {
cols: vec!["index".into(), "item".into()],
vals: vec![
Value::Int {
val: idx as i64,
span,
},
x.clone(),
],
span,
},
);
} else {
stack.add_var(*var_id, x.clone());
}
}
}
// Optional second index argument
if let Some(var) = block.signature.get_positional(1) {
if let Some(var_id) = &var.var_id {
stack.add_var(
*var_id,
Value::Int {
val: idx as i64,
span,
},
);
stack.add_var(*var_id, x.clone());
}
}
@ -214,8 +178,7 @@ with 'transpose' first."#
..
} => Ok(stream
.into_iter()
.enumerate()
.map_while(move |(idx, x)| {
.map_while(move |x| {
// with_env() is used here to ensure that each iteration uses
// a different set of environment variables.
// Hence, a 'cd' in the first loop won't affect the next loop.
@ -229,24 +192,7 @@ with 'transpose' first."#
if let Some(var) = block.signature.get_positional(0) {
if let Some(var_id) = &var.var_id {
if numbered {
stack.add_var(
*var_id,
Value::Record {
cols: vec!["index".into(), "item".into()],
vals: vec![
Value::Int {
val: idx as i64,
span,
},
x.clone(),
],
span,
},
);
} else {
stack.add_var(*var_id, x.clone());
}
stack.add_var(*var_id, x.clone());
}
}

View File

@ -24,20 +24,18 @@ impl Command for EachWhile {
fn signature(&self) -> nu_protocol::Signature {
Signature::build(self.name())
.input_output_types(vec![(
Type::List(Box::new(Type::Any)),
Type::List(Box::new(Type::Any)),
)])
.input_output_types(vec![
(
Type::List(Box::new(Type::Any)),
Type::List(Box::new(Type::Any)),
),
(Type::Table(vec![]), Type::List(Box::new(Type::Any))),
])
.required(
"closure",
SyntaxShape::Closure(Some(vec![SyntaxShape::Any, SyntaxShape::Int])),
"the closure to run",
)
.switch(
"numbered",
"iterate with an index (deprecated; use a two-parameter closure instead)",
Some('n'),
)
.category(Category::Filters)
}
@ -66,7 +64,7 @@ impl Command for EachWhile {
}),
},
Example {
example: r#"[1 2 3] | each while {|el ind| if $el < 2 { $"value ($el) at ($ind)!"} }"#,
example: r#"[1 2 3] | enumerate | each while {|e| if $e.item < 2 { $"value ($e.item) at ($e.index)!"} }"#,
description: "Iterate over each element, printing the matching value and its index",
result: Some(Value::List {
vals: vec![Value::test_string("value 1 at 0!")],
@ -84,7 +82,6 @@ impl Command for EachWhile {
input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
let capture_block: Closure = call.req(engine_state, stack, 0)?;
let numbered = call.has_flag("numbered");
let metadata = input.metadata();
let ctrlc = engine_state.ctrlc.clone();
@ -102,12 +99,9 @@ impl Command for EachWhile {
PipelineData::Value(Value::Range { .. }, ..)
| PipelineData::Value(Value::List { .. }, ..)
| PipelineData::ListStream { .. } => Ok(input
// To enumerate over the input (for the index argument),
// it must be converted into an iterator using into_iter().
// TODO: Could this be changed to .into_interruptible_iter(ctrlc) ?
.into_iter()
.enumerate()
.map_while(move |(idx, x)| {
.map_while(move |x| {
// with_env() is used here to ensure that each iteration uses
// a different set of environment variables.
// Hence, a 'cd' in the first loop won't affect the next loop.
@ -115,36 +109,7 @@ impl Command for EachWhile {
if let Some(var) = block.signature.get_positional(0) {
if let Some(var_id) = &var.var_id {
if numbered {
stack.add_var(
*var_id,
Value::Record {
cols: vec!["index".into(), "item".into()],
vals: vec![
Value::Int {
val: idx as i64,
span,
},
x.clone(),
],
span,
},
);
} else {
stack.add_var(*var_id, x.clone());
}
}
}
// Optional second index argument
if let Some(var) = block.signature.get_positional(1) {
if let Some(var_id) = &var.var_id {
stack.add_var(
*var_id,
Value::Int {
val: idx as i64,
span,
},
);
stack.add_var(*var_id, x.clone());
}
}
@ -175,8 +140,7 @@ impl Command for EachWhile {
..
} => Ok(stream
.into_iter()
.enumerate()
.map_while(move |(idx, x)| {
.map_while(move |x| {
// with_env() is used here to ensure that each iteration uses
// a different set of environment variables.
// Hence, a 'cd' in the first loop won't affect the next loop.
@ -189,24 +153,7 @@ impl Command for EachWhile {
if let Some(var) = block.signature.get_positional(0) {
if let Some(var_id) = &var.var_id {
if numbered {
stack.add_var(
*var_id,
Value::Record {
cols: vec!["index".into(), "item".into()],
vals: vec![
Value::Int {
val: idx as i64,
span,
},
x.clone(),
],
span,
},
);
} else {
stack.add_var(*var_id, x.clone());
}
stack.add_var(*var_id, x.clone());
}
}
@ -257,17 +204,7 @@ impl Command for EachWhile {
#[cfg(test)]
mod test {
use super::*;
use nu_test_support::{nu, pipeline};
#[test]
fn uses_optional_index_argument() {
let actual = nu!(
cwd: ".", pipeline(
r#"[7 8 9 10] | each while {|el ind| $el + $ind } | to nuon"#
));
assert_eq!(actual.out, "[7, 9, 11, 13]");
}
#[test]
fn test_examples() {
use crate::test_examples;

View File

@ -72,8 +72,7 @@ a variable. On the other hand, the "row condition" syntax is not supported."#
// To enumerate over the input (for the index argument),
// it must be converted into an iterator using into_iter().
.into_iter()
.enumerate()
.filter_map(move |(idx, x)| {
.filter_map(move |x| {
// with_env() is used here to ensure that each iteration uses
// a different set of environment variables.
// Hence, a 'cd' in the first loop won't affect the next loop.
@ -84,18 +83,6 @@ a variable. On the other hand, the "row condition" syntax is not supported."#
stack.add_var(*var_id, x.clone());
}
}
// Optional index argument
if let Some(var) = block.signature.get_positional(1) {
if let Some(var_id) = &var.var_id {
stack.add_var(
*var_id,
Value::Int {
val: idx as i64,
span,
},
);
}
}
match eval_block(
&engine_state,
@ -125,8 +112,7 @@ a variable. On the other hand, the "row condition" syntax is not supported."#
..
} => Ok(stream
.into_iter()
.enumerate()
.filter_map(move |(idx, x)| {
.filter_map(move |x| {
// see note above about with_env()
stack.with_env(&orig_env_vars, &orig_env_hidden);
@ -140,18 +126,6 @@ a variable. On the other hand, the "row condition" syntax is not supported."#
stack.add_var(*var_id, x.clone());
}
}
// Optional index argument
if let Some(var) = block.signature.get_positional(1) {
if let Some(var_id) = &var.var_id {
stack.add_var(
*var_id,
Value::Int {
val: idx as i64,
span,
},
);
}
}
match eval_block(
&engine_state,

View File

@ -64,27 +64,37 @@ impl Command for Insert {
],
span: Span::test_data(),
}),
}, Example {
},
Example {
description: "Insert a new column into a table, populating all rows",
example: "[[project, lang]; ['Nushell', 'Rust']] | insert type 'shell'",
result: Some(Value::List { vals: vec![Value::Record { cols: vec!["project".into(), "lang".into(), "type".into()],
vals: vec![Value::test_string("Nushell"), Value::test_string("Rust"), Value::test_string("shell")], span: Span::test_data()}], span: Span::test_data()}),
},
Example {
description: "Insert a column with values equal to their row index, plus the value of 'foo' in each row",
example: "[[foo]; [7] [8] [9]] | insert bar {|el ind| $el.foo + $ind }",
example: "[[foo]; [7] [8] [9]] | enumerate | insert bar {|e| $e.item.foo + $e.index } | flatten",
result: Some(Value::List {
vals: vec![Value::Record {
cols: vec!["foo".into(), "bar".into()],
cols: vec!["index".into(), "foo".into(), "bar".into()],
vals: vec![
Value::test_int(0),
Value::test_int(7),
Value::test_int(7),
],
span: Span::test_data(),
}, Value::Record {
cols: vec!["foo".into(), "bar".into()],
cols: vec!["index".into(),"foo".into(), "bar".into()],
vals: vec![
Value::test_int(1),
Value::test_int(8),
Value::test_int(9),
],
span: Span::test_data(),
}, Value::Record {
cols: vec!["foo".into(), "bar".into()],
cols: vec!["index".into(), "foo".into(), "bar".into()],
vals: vec![
Value::test_int(2),
Value::test_int(9),
Value::test_int(11),
],
@ -122,9 +132,6 @@ fn insert(
let orig_env_vars = stack.env_vars.clone();
let orig_env_hidden = stack.env_hidden.clone();
// enumerate() can't be used here because it converts records into tables
// when combined with into_pipeline_data(). Hence, the index is tracked manually like so.
let mut idx: i64 = 0;
input.map(
move |mut input| {
// with_env() is used here to ensure that each iteration uses
@ -138,13 +145,6 @@ fn insert(
stack.add_var(*var_id, input.clone())
}
}
// Optional index argument
if let Some(var) = block.signature.get_positional(1) {
if let Some(var_id) = &var.var_id {
stack.add_var(*var_id, Value::Int { val: idx, span });
}
idx += 1;
}
let output = eval_block(
&engine_state,

View File

@ -23,20 +23,18 @@ impl Command for ParEach {
fn signature(&self) -> nu_protocol::Signature {
Signature::build("par-each")
.input_output_types(vec![(
Type::List(Box::new(Type::Any)),
Type::List(Box::new(Type::Any)),
)])
.input_output_types(vec![
(
Type::List(Box::new(Type::Any)),
Type::List(Box::new(Type::Any)),
),
(Type::Table(vec![]), Type::List(Box::new(Type::Any))),
])
.required(
"closure",
SyntaxShape::Closure(Some(vec![SyntaxShape::Any, SyntaxShape::Int])),
"the closure to run",
)
.switch(
"numbered",
"iterate with an index (deprecated; use a two-parameter closure instead)",
Some('n'),
)
.category(Category::Filters)
}
@ -49,8 +47,29 @@ impl Command for ParEach {
result: None,
},
Example {
example: r#"[1 2 3] | par-each -n { |it| if $it.item == 2 { $"found 2 at ($it.index)!"} }"#,
description: "Iterate over each element, print the matching value and its index",
example: r#"[foo bar baz] | par-each {|e| $e + '!' } | sort"#,
description: "Output can still be sorted afterward",
result: Some(Value::List {
vals: vec![
Value::test_string("bar!"),
Value::test_string("baz!"),
Value::test_string("foo!"),
],
span: Span::test_data(),
}),
},
Example {
example: r#"1..3 | enumerate | par-each {|p| update item ($p.item * 2)} | sort-by item | get item"#,
description: "Enumerate and sort-by can be used to reconstruct the original order",
result: Some(Value::List {
vals: vec![Value::test_int(2), Value::test_int(4), Value::test_int(6)],
span: Span::test_data(),
}),
},
Example {
example: r#"[1 2 3] | enumerate | par-each { |e| if $e.item == 2 { $"found 2 at ($e.index)!"} }"#,
description:
"Iterate over each element, producing a list showing indexes of any 2s",
result: Some(Value::List {
vals: vec![Value::test_string("found 2 at 1!")],
span: Span::test_data(),
@ -68,12 +87,10 @@ impl Command for ParEach {
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
let capture_block: Closure = call.req(engine_state, stack, 0)?;
let numbered = call.has_flag("numbered");
let metadata = input.metadata();
let ctrlc = engine_state.ctrlc.clone();
let block_id = capture_block.block_id;
let mut stack = stack.captures_to_stack(&capture_block.captures);
let span = call.head;
let redirect_stdout = call.redirect_stdout;
let redirect_stderr = call.redirect_stderr;
@ -81,45 +98,15 @@ impl Command for ParEach {
PipelineData::Empty => Ok(PipelineData::Empty),
PipelineData::Value(Value::Range { val, .. }, ..) => Ok(val
.into_range_iter(ctrlc.clone())?
.enumerate()
.par_bridge()
.map(move |(idx, x)| {
.map(move |x| {
let block = engine_state.get_block(block_id);
let mut stack = stack.clone();
if let Some(var) = block.signature.get_positional(0) {
if let Some(var_id) = &var.var_id {
if numbered {
stack.add_var(
*var_id,
Value::Record {
cols: vec!["index".into(), "item".into()],
vals: vec![
Value::Int {
val: idx as i64,
span,
},
x.clone(),
],
span,
},
);
} else {
stack.add_var(*var_id, x.clone());
}
}
}
// Optional second index argument
if let Some(var) = block.signature.get_positional(1) {
if let Some(var_id) = &var.var_id {
stack.add_var(
*var_id,
Value::Int {
val: idx as i64,
span,
},
);
stack.add_var(*var_id, x.clone());
}
}
@ -145,45 +132,15 @@ impl Command for ParEach {
.into_pipeline_data(ctrlc)),
PipelineData::Value(Value::List { vals: val, .. }, ..) => Ok(val
.into_iter()
.enumerate()
.par_bridge()
.map(move |(idx, x)| {
.map(move |x| {
let block = engine_state.get_block(block_id);
let mut stack = stack.clone();
if let Some(var) = block.signature.get_positional(0) {
if let Some(var_id) = &var.var_id {
if numbered {
stack.add_var(
*var_id,
Value::Record {
cols: vec!["index".into(), "item".into()],
vals: vec![
Value::Int {
val: idx as i64,
span,
},
x.clone(),
],
span,
},
);
} else {
stack.add_var(*var_id, x.clone());
}
}
}
// Optional second index argument
if let Some(var) = block.signature.get_positional(1) {
if let Some(var_id) = &var.var_id {
stack.add_var(
*var_id,
Value::Int {
val: idx as i64,
span,
},
);
stack.add_var(*var_id, x.clone());
}
}
@ -208,45 +165,15 @@ impl Command for ParEach {
.flatten()
.into_pipeline_data(ctrlc)),
PipelineData::ListStream(stream, ..) => Ok(stream
.enumerate()
.par_bridge()
.map(move |(idx, x)| {
.map(move |x| {
let block = engine_state.get_block(block_id);
let mut stack = stack.clone();
if let Some(var) = block.signature.get_positional(0) {
if let Some(var_id) = &var.var_id {
if numbered {
stack.add_var(
*var_id,
Value::Record {
cols: vec!["index".into(), "item".into()],
vals: vec![
Value::Int {
val: idx as i64,
span,
},
x.clone(),
],
span,
},
);
} else {
stack.add_var(*var_id, x.clone());
}
}
}
// Optional second index argument
if let Some(var) = block.signature.get_positional(1) {
if let Some(var_id) = &var.var_id {
stack.add_var(
*var_id,
Value::Int {
val: idx as i64,
span,
},
);
stack.add_var(*var_id, x.clone());
}
}
@ -275,9 +202,8 @@ impl Command for ParEach {
stdout: Some(stream),
..
} => Ok(stream
.enumerate()
.par_bridge()
.map(move |(idx, x)| {
.map(move |x| {
let x = match x {
Ok(x) => x,
Err(err) => return Value::Error { error: err }.into_pipeline_data(),
@ -289,36 +215,7 @@ impl Command for ParEach {
if let Some(var) = block.signature.get_positional(0) {
if let Some(var_id) = &var.var_id {
if numbered {
stack.add_var(
*var_id,
Value::Record {
cols: vec!["index".into(), "item".into()],
vals: vec![
Value::Int {
val: idx as i64,
span,
},
x.clone(),
],
span,
},
);
} else {
stack.add_var(*var_id, x.clone());
}
}
}
// Optional second index argument
if let Some(var) = block.signature.get_positional(1) {
if let Some(var_id) = &var.var_id {
stack.add_var(
*var_id,
Value::Int {
val: idx as i64,
span,
},
);
stack.add_var(*var_id, x.clone());
}
}
@ -366,17 +263,6 @@ impl Command for ParEach {
#[cfg(test)]
mod test {
use super::*;
use nu_test_support::{nu, pipeline};
#[test]
fn uses_optional_index_argument() {
let actual = nu!(
cwd: ".", pipeline(
r#"[7,8,9,10] | par-each {|el ind| $ind } | describe"#
));
assert_eq!(actual.out, "list<int> (stream)");
}
#[test]
fn test_examples() {

View File

@ -16,7 +16,10 @@ impl Command for Reduce {
fn signature(&self) -> Signature {
Signature::build("reduce")
.input_output_types(vec![(Type::List(Box::new(Type::Any)), Type::Any)])
.input_output_types(vec![
(Type::List(Box::new(Type::Any)), Type::Any),
(Type::Table(vec![]), Type::Any),
])
.named(
"fold",
SyntaxShape::Any,
@ -32,11 +35,6 @@ impl Command for Reduce {
])),
"reducing function",
)
.switch(
"numbered",
"iterate with an index (deprecated; use a 3-parameter closure instead)",
Some('n'),
)
}
fn usage(&self) -> &str {
@ -55,9 +53,10 @@ impl Command for Reduce {
result: Some(Value::test_int(10)),
},
Example {
example: "[ 8 7 6 ] | reduce {|it, acc, ind| $acc + $it + $ind }",
example:
"[ 8 7 6 ] | enumerate | reduce -f 0 {|it, acc| $acc + $it.item + $it.index }",
description: "Sum values of a list, plus their indexes",
result: Some(Value::test_int(22)),
result: Some(Value::test_int(24)),
},
Example {
example: "[ 1 2 3 4 ] | reduce -f 10 {|it, acc| $acc + $it }",
@ -70,7 +69,7 @@ impl Command for Reduce {
result: Some(Value::test_string("ArXhur, KXng Xf Xhe BrXXXns")),
},
Example {
example: r#"['foo.gz', 'bar.gz', 'baz.gz'] | reduce -f '' {|str all ind| $"($all)(if $ind != 0 {'; '})($ind + 1)-($str)" }"#,
example: r#"['foo.gz', 'bar.gz', 'baz.gz'] | enumerate | reduce -f '' {|str all| $"($all)(if $str.index != 0 {'; '})($str.index + 1)-($str.item)" }"#,
description:
"Add ascending numbers to each of the filenames, and join with semicolons.",
result: Some(Value::test_string("1-foo.gz; 2-bar.gz; 3-baz.gz")),
@ -88,7 +87,6 @@ impl Command for Reduce {
let span = call.head;
let fold: Option<Value> = call.get_flag(engine_state, stack, "fold")?;
let numbered = call.has_flag("numbered");
let capture_block: Closure = call.req(engine_state, stack, 0)?;
let mut stack = stack.captures_to_stack(&capture_block.captures);
let block = engine_state.get_block(capture_block.block_id);
@ -104,10 +102,10 @@ impl Command for Reduce {
// it must be converted into an iterator using into_iter().
let mut input_iter = input.into_iter();
let (off, start_val) = if let Some(val) = fold {
(0, val)
let start_val = if let Some(val) = fold {
val
} else if let Some(val) = input_iter.next() {
(1, val)
val
} else {
return Err(ShellError::GenericError(
"Expected input".to_string(),
@ -118,41 +116,11 @@ impl Command for Reduce {
));
};
let mut acc = if numbered {
Value::Record {
cols: vec!["index".to_string(), "item".to_string()],
vals: vec![Value::Int { val: 0, span }, start_val],
span,
}
} else {
start_val
};
let mut acc = start_val;
let mut input_iter = input_iter
.enumerate()
.map(|(idx, x)| {
if numbered {
(
idx,
Value::Record {
cols: vec!["index".to_string(), "item".to_string()],
vals: vec![
Value::Int {
val: idx as i64 + off,
span,
},
x,
],
span,
},
)
} else {
(idx, x)
}
})
.peekable();
let mut input_iter = input_iter.peekable();
while let Some((idx, x)) = input_iter.next() {
while let Some(x) = input_iter.next() {
// with_env() is used here to ensure that each iteration uses
// a different set of environment variables.
// Hence, a 'cd' in the first loop won't affect the next loop.
@ -168,41 +136,9 @@ impl Command for Reduce {
// Accumulator argument
if let Some(var) = block.signature.get_positional(1) {
if let Some(var_id) = &var.var_id {
acc = if numbered {
if let Value::Record { .. } = &acc {
acc
} else {
Value::Record {
cols: vec!["index".to_string(), "item".to_string()],
vals: vec![
Value::Int {
val: idx as i64 + off,
span,
},
acc,
],
span,
}
}
} else {
acc
};
stack.add_var(*var_id, acc);
}
}
// Optional third index argument
if let Some(var) = block.signature.get_positional(2) {
if let Some(var_id) = &var.var_id {
stack.add_var(
*var_id,
Value::Int {
val: idx as i64,
span,
},
);
}
}
acc = eval_block(
engine_state,

View File

@ -60,7 +60,7 @@ impl Command for Update {
},
Example {
description: "Use in closure form for more involved updating logic",
example: "[[count fruit]; [1 'apple']] | update count {|row index| ($row.fruit | str length) + $index }",
example: "[[count fruit]; [1 'apple']] | enumerate | update item.count {|e| ($e.item.fruit | str length) + $e.index } | get item",
result: Some(Value::List {
vals: vec![Value::Record {
cols: vec!["count".into(), "fruit".into()],
@ -105,9 +105,6 @@ fn update(
let orig_env_vars = stack.env_vars.clone();
let orig_env_hidden = stack.env_hidden.clone();
// enumerate() can't be used here because it converts records into tables
// when combined with into_pipeline_data(). Hence, the index is tracked manually like so.
let mut idx: i64 = 0;
input.map(
move |mut input| {
// with_env() is used here to ensure that each iteration uses
@ -120,13 +117,6 @@ fn update(
stack.add_var(*var_id, input.clone())
}
}
// Optional index argument
if let Some(var) = block.signature.get_positional(1) {
if let Some(var_id) = &var.var_id {
stack.add_var(*var_id, Value::Int { val: idx, span });
}
idx += 1;
}
let output = eval_block(
&engine_state,

View File

@ -56,14 +56,25 @@ impl Command for Upsert {
description: "Update a record's value",
example: "{'name': 'nu', 'stars': 5} | upsert name 'Nushell'",
result: Some(Value::Record { cols: vec!["name".into(), "stars".into()], vals: vec![Value::test_string("Nushell"), Value::test_int(5)], span: Span::test_data()}),
}, Example {
},
Example {
description: "Update each row of a table",
example: "[[name lang]; [Nushell ''] [Reedline '']] | upsert lang 'Rust'",
result: Some(Value::List { vals: vec![
Value::Record { cols: vec!["name".into(), "lang".into()], vals: vec![Value::test_string("Nushell"), Value::test_string("Rust")], span: Span::test_data()},
Value::Record { cols: vec!["name".into(), "lang".into()], vals: vec![Value::test_string("Reedline"), Value::test_string("Rust")], span: Span::test_data()}
], span: Span::test_data()}),
},
Example {
description: "Insert a new entry into a single record",
example: "{'name': 'nu', 'stars': 5} | upsert language 'Rust'",
result: Some(Value::Record { cols: vec!["name".into(), "stars".into(), "language".into()], vals: vec![Value::test_string("nu"), Value::test_int(5), Value::test_string("Rust")], span: Span::test_data()}),
}, Example {
description: "Use in closure form for more involved updating logic",
example: "[[count fruit]; [1 'apple']] | upsert count {|row index| ($row.fruit | str length) + $index }",
result: Some(Value::List { vals: vec![Value::Record { cols: vec!["count".into(), "fruit".into()], vals: vec![Value::test_int(5), Value::test_string("apple")], span: Span::test_data()}], span: Span::test_data()}),
example: "[[count fruit]; [1 'apple']] | enumerate | upsert item.count {|e| ($e.item.fruit | str length) + $e.index } | get item",
result: Some(Value::List { vals: vec![
Value::Record { cols: vec!["count".into(), "fruit".into()], vals: vec![Value::test_int(5), Value::test_string("apple")], span: Span::test_data()}],
span: Span::test_data()}),
},
Example {
description: "Upsert an int into a list, updating an existing value based on the index",
@ -116,9 +127,6 @@ fn upsert(
let orig_env_vars = stack.env_vars.clone();
let orig_env_hidden = stack.env_hidden.clone();
// enumerate() can't be used here because it converts records into tables
// when combined with into_pipeline_data(). Hence, the index is tracked manually like so.
let mut idx: i64 = 0;
input.map(
move |mut input| {
// with_env() is used here to ensure that each iteration uses
@ -131,13 +139,6 @@ fn upsert(
stack.add_var(*var_id, input.clone())
}
}
// Optional index argument
if let Some(var) = block.signature.get_positional(1) {
if let Some(var_id) = &var.var_id {
stack.add_var(*var_id, Value::Int { val: idx, span });
}
idx += 1;
}
let output = eval_block(
&engine_state,

View File

@ -70,8 +70,7 @@ not supported."#
let redirect_stderr = call.redirect_stderr;
Ok(input
.into_iter_strict(span)?
.enumerate()
.filter_map(move |(idx, value)| {
.filter_map(move |value| {
stack.with_env(&orig_env_vars, &orig_env_hidden);
if let Some(var) = block.signature.get_positional(0) {
@ -79,18 +78,6 @@ not supported."#
stack.add_var(*var_id, value.clone());
}
}
// Optional index argument
if let Some(var) = block.signature.get_positional(1) {
if let Some(var_id) = &var.var_id {
stack.add_var(
*var_id,
Value::Int {
val: idx as i64,
span,
},
);
}
}
let result = eval_block(
&engine_state,
&mut stack,
@ -159,6 +146,11 @@ not supported."#
example: "ls | where modified >= (date now) - 2wk",
result: None,
},
Example {
description: "Find files whose filenames don't begin with the correct sequential number",
example: "ls | where type == file | sort-by name -n | enumerate | where {|e| $e.item.name !~ $'^($e.index + 1)' } | each { get item }",
result: None,
},
]
}
}

View File

@ -109,10 +109,10 @@ fn early_exits_with_0_param_blocks() {
}
#[test]
fn uses_optional_index_argument() {
fn all_uses_enumerate_index() {
let actual = nu!(
cwd: ".", pipeline(
r#"[7 8 9] | all {|el ind| print $ind | true }"#
r#"[7 8 9] | enumerate | all {|el| print $el.index | true }"#
));
assert_eq!(actual.out, "012true");

View File

@ -85,10 +85,10 @@ fn early_exits_with_0_param_blocks() {
}
#[test]
fn uses_optional_index_argument() {
fn any_uses_enumerate_index() {
let actual = nu!(
cwd: ".", pipeline(
r#"[7 8 9] | any {|el ind| print $ind | false }"#
r#"[7 8 9] | enumerate | any {|el| print $el.index | false }"#
));
assert_eq!(actual.out, "012false");

View File

@ -73,20 +73,20 @@ fn each_implicit_it_in_block() {
}
#[test]
fn uses_optional_index_argument() {
fn each_uses_enumerate_index() {
let actual = nu!(
cwd: ".", pipeline(
r#"[7 8 9 10] | each {|el ind| $ind } | to nuon"#
r#"[7 8 9 10] | enumerate | each {|el| $el.index } | to nuon"#
));
assert_eq!(actual.out, "[0, 1, 2, 3]");
}
#[test]
fn each_while_uses_optional_index_argument() {
fn each_while_uses_enumerate_index() {
let actual = nu!(
cwd: ".", pipeline(
r#"[7 8 9 10] | each while {|el ind| $ind } | to nuon"#
r#"[7 8 9 10] | enumerate | each while {|el| $el.index } | to nuon"#
));
assert_eq!(actual.out, "[0, 1, 2, 3]");

View File

@ -87,11 +87,11 @@ fn insert_past_end_list() {
}
#[test]
fn uses_optional_index_argument() {
fn insert_uses_enumerate_index() {
let actual = nu!(
cwd: ".", pipeline(
r#"[[a]; [7] [6]] | insert b {|el ind| $ind + 1 + $el.a } | to nuon"#
r#"[[a]; [7] [6]] | enumerate | insert b {|el| $el.index + 1 + $el.item.a } | flatten | to nuon"#
));
assert_eq!(actual.out, "[[a, b]; [7, 8], [6, 8]]");
assert_eq!(actual.out, "[[index, a, b]; [0, 7, 8], [1, 6, 8]]");
}

View File

@ -47,12 +47,12 @@ fn reduce_rows_example() {
}
#[test]
fn reduce_numbered_example() {
fn reduce_enumerate_example() {
let actual = nu!(
cwd: ".", pipeline(
r#"
echo one longest three bar
| reduce -n { |it, acc| if ($it.item | str length) > ($acc.item | str length) {echo $it} else {echo $acc}}
echo one longest three bar | enumerate
| reduce { |it, acc| if ($it.item | str length) > ($acc.item | str length) {echo $it} else {echo $acc}}
| get index
"#
)
@ -62,12 +62,14 @@ fn reduce_numbered_example() {
}
#[test]
fn reduce_numbered_integer_addition_example() {
fn reduce_enumerate_integer_addition_example() {
let actual = nu!(
cwd: ".", pipeline(
r#"
echo [1 2 3 4]
| reduce -n { |it, acc| $acc.item + $it.item }
| enumerate
| reduce { |it, acc| { index: ($it.index) item: ($acc.item + $it.item)} }
| get item
"#
)
);
@ -121,11 +123,14 @@ fn error_reduce_empty() {
}
#[test]
fn uses_optional_index_argument() {
fn enumerate_reduce_example() {
let actual = nu!(
cwd: ".", pipeline(
r#"[18 19 20] | reduce -f 0 {|elem accum index| $accum + $index } | to nuon"#
));
r#"
[one longest three bar] | enumerate | reduce {|it, acc| if ($it.item | str length) > ($acc.item | str length) { $it } else { $acc }} | get index
"#
)
);
assert_eq!(actual.out, "3");
assert_eq!(actual.out, "1");
}

View File

@ -135,7 +135,8 @@ mod columns {
transpose bit --ignore-titles
| get bit
| reverse
| each --numbered { |it|
| enumerate
| each { |it|
$it.item * (2 ** $it.index)
}
| math sum

View File

@ -116,11 +116,11 @@ fn update_nonexistent_column() {
}
#[test]
fn uses_optional_index_argument() {
fn update_uses_enumerate_index() {
let actual = nu!(
cwd: ".", pipeline(
r#"[[a]; [7] [6]] | update a {|el ind| $ind + 1 + $el.a } | to nuon"#
r#"[[a]; [7] [6]] | enumerate | update item.a {|el| $el.index + 1 + $el.item.a } | flatten | to nuon"#
));
assert_eq!(actual.out, "[[a]; [8], [8]]");
assert_eq!(actual.out, "[[index, a]; [0, 8], [1, 8]]");
}

View File

@ -69,23 +69,23 @@ fn sets_the_column_from_a_subexpression() {
}
#[test]
fn uses_optional_index_argument_inserting() {
fn upsert_uses_enumerate_index_inserting() {
let actual = nu!(
cwd: ".", pipeline(
r#"[[a]; [7] [6]] | upsert b {|el ind| $ind + 1 + $el.a } | to nuon"#
r#"[[a]; [7] [6]] | enumerate | upsert b {|el| $el.index + 1 + $el.item.a } | flatten | to nuon"#
));
assert_eq!(actual.out, "[[a, b]; [7, 8], [6, 8]]");
assert_eq!(actual.out, "[[index, a, b]; [0, 7, 8], [1, 6, 8]]");
}
#[test]
fn uses_optional_index_argument_updating() {
fn upsert_uses_enumerate_index_updating() {
let actual = nu!(
cwd: ".", pipeline(
r#"[[a]; [7] [6]] | upsert a {|el ind| $ind + 1 + $el.a } | to nuon"#
r#"[[a]; [7] [6]] | enumerate | upsert a {|el| $el.index + 1 + $el.item.a } | flatten | to nuon"#
));
assert_eq!(actual.out, "[[a]; [8], [8]]");
assert_eq!(actual.out, "[[index, a]; [0, 8], [1, 8]]");
}
#[test]

View File

@ -82,13 +82,13 @@ fn where_not_in_table() {
}
#[test]
fn uses_optional_index_argument() {
fn where_uses_enumerate_index() {
let actual = nu!(
cwd: ".",
r#"[7 8 9 10] | where {|el ind| $ind < 2 } | to nuon"#
r#"[7 8 9 10] | enumerate | where {|el| $el.index < 2 } | to nuon"#
);
assert_eq!(actual.out, "[7, 8]");
assert_eq!(actual.out, "[[index, item]; [0, 7], [1, 8]]");
}
#[cfg(feature = "sqlite")]

View File

@ -3,7 +3,7 @@ use crate::tests::{run_test, TestResult};
#[test]
fn better_block_types() -> TestResult {
run_test(
r#"([1, 2, 3] | each -n { |it| $"($it.index) is ($it.item)" }).1"#,
r#"([1, 2, 3] | enumerate | each { |e| $"($e.index) is ($e.item)" }).1"#,
"1 is 2",
)
}
@ -35,7 +35,7 @@ fn row_condition2() -> TestResult {
#[test]
fn par_each() -> TestResult {
run_test(
r#"1..10 | par-each --numbered { |it| ([[index, item]; [$it.index, ($it.item > 5)]]).0 } | where index == 4 | get item.0"#,
r#"1..10 | enumerate | par-each { |it| ([[index, item]; [$it.index, ($it.item > 5)]]).0 } | where index == 4 | get item.0"#,
"false",
)
}

View File

@ -147,8 +147,8 @@ fn bad_var_name2() -> TestResult {
#[test]
fn long_flag() -> TestResult {
run_test(
r#"([a, b, c] | each --numbered { |it| if $it.index == 1 { 100 } else { 0 } }).1"#,
"100",
r#"([a, b, c] | enumerate | each --keep-empty { |e| if $e.index != 1 { 100 }}).1 | to nuon"#,
"null",
)
}