From 53a6e9f0bda804d2caec17c8f73075e1220f3b1b Mon Sep 17 00:00:00 2001 From: "Joseph T. Lyons" Date: Thu, 18 Jun 2020 22:02:01 -0400 Subject: [PATCH] Convert sum command into subcommand of the math command (#2004) * Convert sum command into subcommand of the math command * Add bullet points to math.md documentation --- crates/nu-cli/src/cli.rs | 2 +- crates/nu-cli/src/commands.rs | 4 +- crates/nu-cli/src/commands/each.rs | 2 +- crates/nu-cli/src/commands/math/average.rs | 4 +- crates/nu-cli/src/commands/math/command.rs | 24 +++- crates/nu-cli/src/commands/math/max.rs | 2 +- crates/nu-cli/src/commands/math/median.rs | 2 +- crates/nu-cli/src/commands/math/mod.rs | 2 + crates/nu-cli/src/commands/{ => math}/sum.rs | 81 ++++++------ crates/nu-cli/src/utils/data_processing.rs | 8 +- crates/nu-cli/tests/commands/drop.rs | 2 +- crates/nu-cli/tests/commands/each.rs | 2 +- crates/nu-cli/tests/commands/is_empty.rs | 6 +- crates/nu-cli/tests/commands/keep.rs | 2 +- crates/nu-cli/tests/commands/keep_until.rs | 2 +- crates/nu-cli/tests/commands/keep_while.rs | 2 +- crates/nu-cli/tests/commands/merge.rs | 2 +- crates/nu-cli/tests/commands/skip_until.rs | 2 +- crates/nu-cli/tests/commands/str_.rs | 4 +- crates/nu-cli/tests/commands/sum.rs | 10 +- crates/nu-cli/tests/commands/where_.rs | 6 +- crates/nu-test-support/src/lib.rs | 4 +- docs/commands/math.md | 129 +++++++++++-------- docs/commands/str.md | 2 +- docs/commands/sum.md | 10 +- tests/shell/pipeline/commands/internal.rs | 2 +- 26 files changed, 175 insertions(+), 143 deletions(-) rename crates/nu-cli/src/commands/{ => math}/sum.rs (50%) diff --git a/crates/nu-cli/src/cli.rs b/crates/nu-cli/src/cli.rs index e3cf9100..ab83a4a9 100644 --- a/crates/nu-cli/src/cli.rs +++ b/crates/nu-cli/src/cli.rs @@ -352,7 +352,7 @@ pub fn create_default_context( whole_stream_command(MathMedian), whole_stream_command(MathMinimum), whole_stream_command(MathMaximum), - whole_stream_command(Sum), + whole_stream_command(MathSummation), // File format output whole_stream_command(To), whole_stream_command(ToBSON), diff --git a/crates/nu-cli/src/commands.rs b/crates/nu-cli/src/commands.rs index d24be557..cc79777f 100644 --- a/crates/nu-cli/src/commands.rs +++ b/crates/nu-cli/src/commands.rs @@ -102,7 +102,6 @@ pub(crate) mod sort_by; pub(crate) mod split; pub(crate) mod split_by; pub(crate) mod str_; -pub(crate) mod sum; #[allow(unused)] pub(crate) mod t_sort_by; pub(crate) mod table; @@ -199,7 +198,7 @@ pub(crate) use lines::Lines; pub(crate) use ls::Ls; #[allow(unused_imports)] pub(crate) use map_max_by::MapMaxBy; -pub(crate) use math::{Math, MathAverage, MathMaximum, MathMedian, MathMinimum}; +pub(crate) use math::{Math, MathAverage, MathMaximum, MathMedian, MathMinimum, MathSummation}; pub(crate) use merge::Merge; pub(crate) use mkdir::Mkdir; pub(crate) use mv::Move; @@ -236,7 +235,6 @@ pub(crate) use str_::{ Str, StrCapitalize, StrDowncase, StrFindReplace, StrSet, StrSubstring, StrToDatetime, StrToDecimal, StrToInteger, StrTrim, StrUpcase, }; -pub(crate) use sum::Sum; #[allow(unused_imports)] pub(crate) use t_sort_by::TSortBy; pub(crate) use table::Table; diff --git a/crates/nu-cli/src/commands/each.rs b/crates/nu-cli/src/commands/each.rs index 10cae1a4..76eb5adf 100644 --- a/crates/nu-cli/src/commands/each.rs +++ b/crates/nu-cli/src/commands/each.rs @@ -56,7 +56,7 @@ impl WholeStreamCommand for Each { }, Example { description: "Echo the sum of each row", - example: "echo [[1 2] [3 4]] | each { echo $it | sum }", + example: "echo [[1 2] [3 4]] | each { echo $it | math sum }", result: Some(vec![ UntaggedValue::int(3).into(), UntaggedValue::int(7).into(), diff --git a/crates/nu-cli/src/commands/math/average.rs b/crates/nu-cli/src/commands/math/average.rs index 0e02ab1a..ecb111cd 100644 --- a/crates/nu-cli/src/commands/math/average.rs +++ b/crates/nu-cli/src/commands/math/average.rs @@ -22,7 +22,7 @@ impl WholeStreamCommand for SubCommand { } fn usage(&self) -> &str { - "Gets the average of a list of numbers" + "Finds the average of a list of numbers or tables" } async fn run( @@ -56,7 +56,7 @@ impl WholeStreamCommand for SubCommand { } pub fn average(values: &[Value], name: &Tag) -> Result { - let sum = reducer_for(Reduce::Sum); + let sum = reducer_for(Reduce::Summation); let number = BigDecimal::from_usize(values.len()).ok_or_else(|| { ShellError::labeled_error( diff --git a/crates/nu-cli/src/commands/math/command.rs b/crates/nu-cli/src/commands/math/command.rs index 25322066..b6609754 100644 --- a/crates/nu-cli/src/commands/math/command.rs +++ b/crates/nu-cli/src/commands/math/command.rs @@ -35,7 +35,7 @@ impl WholeStreamCommand for Command { mod tests { use super::*; use crate::commands::math::{ - average::average, max::maximum, min::minimum, utils::MathFunction, + average::average, max::maximum, min::minimum, sum::summation, utils::MathFunction, }; use nu_plugin::test_helpers::value::{decimal, int}; use nu_protocol::Value; @@ -67,31 +67,41 @@ mod tests { description: "Single value", values: vec![int(10)], expected_err: None, - expected_res: vec![Ok(decimal(10)), Ok(int(10)), Ok(int(10))], + expected_res: vec![Ok(decimal(10)), Ok(int(10)), Ok(int(10)), Ok(int(10))], }, TestCase { description: "Multiple Values", values: vec![int(10), int(30), int(20)], expected_err: None, - expected_res: vec![Ok(decimal(20)), Ok(int(10)), Ok(int(30))], + expected_res: vec![Ok(decimal(20)), Ok(int(10)), Ok(int(30)), Ok(int(60))], }, TestCase { description: "Mixed Values", values: vec![int(10), decimal(26.5), decimal(26.5)], expected_err: None, - expected_res: vec![Ok(decimal(21)), Ok(int(10)), Ok(decimal(26.5))], + expected_res: vec![ + Ok(decimal(21)), + Ok(int(10)), + Ok(decimal(26.5)), + Ok(decimal(63)), + ], }, TestCase { description: "Negative Values", values: vec![int(10), int(-11), int(-14)], expected_err: None, - expected_res: vec![Ok(decimal(-5)), Ok(int(-14)), Ok(int(10))], + expected_res: vec![Ok(decimal(-5)), Ok(int(-14)), Ok(int(10)), Ok(int(-15))], }, TestCase { description: "Mixed Negative Values", values: vec![int(10), decimal(-11.5), decimal(-13.5)], expected_err: None, - expected_res: vec![Ok(decimal(-5)), Ok(decimal(-13.5)), Ok(int(10))], + expected_res: vec![ + Ok(decimal(-5)), + Ok(decimal(-13.5)), + Ok(int(10)), + Ok(decimal(-15)), + ], }, // TODO-Uncomment once I figure out how to structure tables // TestCase { @@ -116,7 +126,7 @@ mod tests { for tc in tt.iter() { let tc: &TestCase = tc; // Just for type annotations - let math_functions: Vec = vec![average, minimum, maximum]; + let math_functions: Vec = vec![average, minimum, maximum, summation]; let results = math_functions .iter() .map(|mf| mf(&tc.values, &test_tag)) diff --git a/crates/nu-cli/src/commands/math/max.rs b/crates/nu-cli/src/commands/math/max.rs index db8b6ecd..8d535445 100644 --- a/crates/nu-cli/src/commands/math/max.rs +++ b/crates/nu-cli/src/commands/math/max.rs @@ -18,7 +18,7 @@ impl WholeStreamCommand for SubCommand { } fn usage(&self) -> &str { - "Get the maximum of a list of numbers or tables" + "Finds the maximum within a list of numbers or tables" } async fn run( diff --git a/crates/nu-cli/src/commands/math/median.rs b/crates/nu-cli/src/commands/math/median.rs index 5a212be9..3748e010 100644 --- a/crates/nu-cli/src/commands/math/median.rs +++ b/crates/nu-cli/src/commands/math/median.rs @@ -121,7 +121,7 @@ pub fn median(values: &[Value], name: &Tag) -> Result { fn compute_average(values: &[Value], name: impl Into) -> Result { let name = name.into(); - let sum = reducer_for(Reduce::Sum); + let sum = reducer_for(Reduce::Summation); let number = BigDecimal::from_usize(2).ok_or_else(|| { ShellError::labeled_error( "could not convert to big decimal", diff --git a/crates/nu-cli/src/commands/math/mod.rs b/crates/nu-cli/src/commands/math/mod.rs index 2b0ae6d5..399416fb 100644 --- a/crates/nu-cli/src/commands/math/mod.rs +++ b/crates/nu-cli/src/commands/math/mod.rs @@ -3,6 +3,7 @@ pub mod command; pub mod max; pub mod median; pub mod min; +pub mod sum; pub mod utils; pub use average::SubCommand as MathAverage; @@ -10,3 +11,4 @@ pub use command::Command as Math; pub use max::SubCommand as MathMaximum; pub use median::SubCommand as MathMedian; pub use min::SubCommand as MathMinimum; +pub use sum::SubCommand as MathSummation; diff --git a/crates/nu-cli/src/commands/sum.rs b/crates/nu-cli/src/commands/math/sum.rs similarity index 50% rename from crates/nu-cli/src/commands/sum.rs rename to crates/nu-cli/src/commands/math/sum.rs index b326b72e..01fa5276 100644 --- a/crates/nu-cli/src/commands/sum.rs +++ b/crates/nu-cli/src/commands/math/sum.rs @@ -1,26 +1,25 @@ +use crate::commands::math::utils::calculate; use crate::commands::WholeStreamCommand; use crate::prelude::*; use crate::utils::data_processing::{reducer_for, Reduce}; use nu_errors::ShellError; -use nu_protocol::{Dictionary, ReturnSuccess, Signature, UntaggedValue, Value}; +use nu_protocol::{Dictionary, Signature, UntaggedValue, Value}; use num_traits::identities::Zero; -use indexmap::map::IndexMap; - -pub struct Sum; +pub struct SubCommand; #[async_trait] -impl WholeStreamCommand for Sum { +impl WholeStreamCommand for SubCommand { fn name(&self) -> &str { - "sum" + "math sum" } fn signature(&self) -> Signature { - Signature::build("sum") + Signature::build("math sum") } fn usage(&self) -> &str { - "Sums the values." + "Finds the sum of a list of numbers or tables" } async fn run( @@ -28,16 +27,19 @@ impl WholeStreamCommand for Sum { args: CommandArgs, registry: &CommandRegistry, ) -> Result { - sum(RunnableContext { - input: args.input, - registry: registry.clone(), - shell_manager: args.shell_manager, - host: args.host, - ctrl_c: args.ctrl_c, - current_errors: args.current_errors, - name: args.call_info.name_tag, - raw_input: args.raw_input, - }) + calculate( + RunnableContext { + input: args.input, + registry: registry.clone(), + shell_manager: args.shell_manager, + host: args.host, + ctrl_c: args.ctrl_c, + current_errors: args.current_errors, + name: args.call_info.name_tag, + raw_input: args.raw_input, + }, + summation, + ) .await } @@ -45,31 +47,28 @@ impl WholeStreamCommand for Sum { vec![ Example { description: "Sum a list of numbers", - example: "echo [1 2 3] | sum", + example: "echo [1 2 3] | math sum", result: Some(vec![UntaggedValue::int(6).into()]), }, Example { description: "Get the disk usage for the current directory", - example: "ls --all --du | get size | sum", + example: "ls --all --du | get size | math sum", result: None, }, ] } } -async fn sum( - RunnableContext { mut input, .. }: RunnableContext, -) -> Result { - let values: Vec = input.drain_vec().await; - let action = reducer_for(Reduce::Sum); +pub fn summation(values: &[Value], name: &Tag) -> Result { + let sum = reducer_for(Reduce::Summation); if values.iter().all(|v| v.is_primitive()) { - let total = action(Value::zero(), values)?; - Ok(OutputStream::one(ReturnSuccess::value(total))) + Ok(sum(Value::zero(), values.to_vec())?) } else { let mut column_values = IndexMap::new(); + for value in values { - if let UntaggedValue::Row(row_dict) = value.value { + if let UntaggedValue::Row(row_dict) = value.value.clone() { for (key, value) in row_dict.entries.iter() { column_values .entry(key.clone()) @@ -80,32 +79,28 @@ async fn sum( } let mut column_totals = IndexMap::new(); + for (col_name, col_vals) in column_values { - let sum = action(Value::zero(), col_vals); - match sum { - Ok(value) => { - column_totals.insert(col_name, value); - } - Err(err) => return Err(err), - }; + let sum = sum(Value::zero(), col_vals)?; + + column_totals.insert(col_name, sum); } - Ok(OutputStream::one(ReturnSuccess::value( - UntaggedValue::Row(Dictionary { - entries: column_totals, - }) - .into_untagged_value(), - ))) + + Ok(UntaggedValue::Row(Dictionary { + entries: column_totals, + }) + .into_value(name)) } } #[cfg(test)] mod tests { - use super::Sum; + use super::SubCommand; #[test] fn examples_work_as_expected() { use crate::examples::test as test_examples; - test_examples(Sum {}) + test_examples(SubCommand {}) } } diff --git a/crates/nu-cli/src/utils/data_processing.rs b/crates/nu-cli/src/utils/data_processing.rs index b17f7143..8b94f904 100644 --- a/crates/nu-cli/src/utils/data_processing.rs +++ b/crates/nu-cli/src/utils/data_processing.rs @@ -288,14 +288,14 @@ pub fn reducer_for( command: Reduce, ) -> Box) -> Result + Send + Sync + 'static> { match command { - Reduce::Sum | Reduce::Default => Box::new(formula(Value::zero(), Box::new(sum))), + Reduce::Summation | Reduce::Default => Box::new(formula(Value::zero(), Box::new(sum))), Reduce::Minimum => Box::new(|_, values| min(values)), Reduce::Maximum => Box::new(|_, values| max(values)), } } pub enum Reduce { - Sum, + Summation, Minimum, Maximum, Default, @@ -309,7 +309,7 @@ pub fn reduce( let tag = tag.into(); let reduce_with = match reducer { - Some(cmd) if cmd == "sum" => reducer_for(Reduce::Sum), + Some(cmd) if cmd == "sum" => reducer_for(Reduce::Summation), Some(cmd) if cmd == "min" => reducer_for(Reduce::Minimum), Some(cmd) if cmd == "max" => reducer_for(Reduce::Maximum), Some(_) | None => reducer_for(Reduce::Default), @@ -642,7 +642,7 @@ mod tests { fn reducer_computes_given_a_sum_command() -> Result<(), ShellError> { let subject = vec![int(1), int(1), int(1)]; - let action = reducer_for(Reduce::Sum); + let action = reducer_for(Reduce::Summation); assert_eq!(action(Value::zero(), subject)?, int(3)); diff --git a/crates/nu-cli/tests/commands/drop.rs b/crates/nu-cli/tests/commands/drop.rs index 1bdb26aa..432f94f6 100644 --- a/crates/nu-cli/tests/commands/drop.rs +++ b/crates/nu-cli/tests/commands/drop.rs @@ -4,7 +4,7 @@ use nu_test_support::nu; fn drop_rows() { let actual = nu!( cwd: "tests/fixtures/formats", - r#"echo '[{"foo": 3}, {"foo": 8}, {"foo": 4}]' | from json | drop 2 | get foo | sum | echo $it"# + r#"echo '[{"foo": 3}, {"foo": 8}, {"foo": 4}]' | from json | drop 2 | get foo | math sum | echo $it"# ); assert_eq!(actual.out, "3"); diff --git a/crates/nu-cli/tests/commands/each.rs b/crates/nu-cli/tests/commands/each.rs index bfffe2bf..f12ffe3b 100644 --- a/crates/nu-cli/tests/commands/each.rs +++ b/crates/nu-cli/tests/commands/each.rs @@ -5,7 +5,7 @@ fn each_works_separately() { let actual = nu!( cwd: "tests/fixtures/formats", pipeline( r#" - echo [1 2 3] | each { echo $it 10 | sum } | to json | echo $it + echo [1 2 3] | each { echo $it 10 | math sum } | to json | echo $it "# )); diff --git a/crates/nu-cli/tests/commands/is_empty.rs b/crates/nu-cli/tests/commands/is_empty.rs index c53192f2..6929a58d 100644 --- a/crates/nu-cli/tests/commands/is_empty.rs +++ b/crates/nu-cli/tests/commands/is_empty.rs @@ -22,7 +22,7 @@ fn adds_value_provided_if_column_is_empty() { open likes.csv | empty? likes 1 | get likes - | sum + | math sum | echo $it "# )); @@ -43,7 +43,7 @@ fn adds_value_provided_for_columns_that_are_empty() { {"boost": 1, "check": {}}, {"boost": null, "check": ["" {} [] ""]} ] - + "#, )]); @@ -53,7 +53,7 @@ fn adds_value_provided_for_columns_that_are_empty() { open checks.json | empty? boost check 1 | get boost check - | sum + | math sum | echo $it "# )); diff --git a/crates/nu-cli/tests/commands/keep.rs b/crates/nu-cli/tests/commands/keep.rs index 540566c8..eb500251 100644 --- a/crates/nu-cli/tests/commands/keep.rs +++ b/crates/nu-cli/tests/commands/keep.rs @@ -22,7 +22,7 @@ fn rows() { open caballeros.csv | keep 3 | get lucky_code - | sum + | math sum | echo $it "# )); diff --git a/crates/nu-cli/tests/commands/keep_until.rs b/crates/nu-cli/tests/commands/keep_until.rs index 55524538..6dab4273 100644 --- a/crates/nu-cli/tests/commands/keep_until.rs +++ b/crates/nu-cli/tests/commands/keep_until.rs @@ -41,7 +41,7 @@ fn condition_is_met() { | keep-until "Chicken Collection" == "Red Chickens" | str to-int "31/04/2020" | get "31/04/2020" - | sum + | math sum | echo $it "# )); diff --git a/crates/nu-cli/tests/commands/keep_while.rs b/crates/nu-cli/tests/commands/keep_while.rs index e8c3e693..87eeaade 100644 --- a/crates/nu-cli/tests/commands/keep_while.rs +++ b/crates/nu-cli/tests/commands/keep_while.rs @@ -41,7 +41,7 @@ fn condition_is_met() { | keep-while "Chicken Collection" != "Blue Chickens" | str to-int "31/04/2020" | get "31/04/2020" - | sum + | math sum | echo $it "# )); diff --git a/crates/nu-cli/tests/commands/merge.rs b/crates/nu-cli/tests/commands/merge.rs index dfda69ee..cda5a917 100644 --- a/crates/nu-cli/tests/commands/merge.rs +++ b/crates/nu-cli/tests/commands/merge.rs @@ -33,7 +33,7 @@ fn row() { | merge { open new_caballeros.csv } | where country in: ["Guayaquil Ecuador" "New Zealand"] | get luck - | sum + | math sum | echo $it "# )); diff --git a/crates/nu-cli/tests/commands/skip_until.rs b/crates/nu-cli/tests/commands/skip_until.rs index 4748766c..1344f49d 100644 --- a/crates/nu-cli/tests/commands/skip_until.rs +++ b/crates/nu-cli/tests/commands/skip_until.rs @@ -40,7 +40,7 @@ fn condition_is_met() { | skip-until "Chicken Collection" == "Red Chickens" | str to-int "31/04/2020" | get "31/04/2020" - | sum + | math sum | echo $it "# )); diff --git a/crates/nu-cli/tests/commands/str_.rs b/crates/nu-cli/tests/commands/str_.rs index e25e5df2..3f2f574a 100644 --- a/crates/nu-cli/tests/commands/str_.rs +++ b/crates/nu-cli/tests/commands/str_.rs @@ -108,7 +108,7 @@ fn converts_to_decimal() { echo "3.1, 0.0415" | split row "," | str to-decimal - | sum + | math sum "# )); @@ -130,7 +130,7 @@ fn sets() { cwd: dirs.test(), pipeline( r#" open sample.toml - | str set wykittenshell package.name + | str set wykittenshell package.name | get package.name | echo $it "# diff --git a/crates/nu-cli/tests/commands/sum.rs b/crates/nu-cli/tests/commands/sum.rs index 5d5e440c..7395ef6c 100644 --- a/crates/nu-cli/tests/commands/sum.rs +++ b/crates/nu-cli/tests/commands/sum.rs @@ -25,7 +25,7 @@ fn all() { open meals.json | get meals | get calories - | sum + | math sum | echo $it "# )); @@ -53,7 +53,7 @@ fn outputs_zero_with_no_input() { let actual = nu!( cwd: dirs.test(), pipeline( r#" - sum + math sum | echo $it "# )); @@ -74,7 +74,7 @@ fn compute_sum_of_individual_row() -> Result<(), String> { for (column_name, expected_value) in answers_for_columns.iter() { let actual = nu!( cwd: "tests/fixtures/formats/", - format!("open sample-ps-output.json | select {} | sum | get {}", column_name, column_name) + format!("open sample-ps-output.json | select {} | math sum | get {}", column_name, column_name) ); let result = f64::from_str(&actual.out).map_err(|_| String::from("Failed to parse float."))?; @@ -95,7 +95,7 @@ fn compute_sum_of_table() -> Result<(), String> { for (column_name, expected_value) in answers_for_columns.iter() { let actual = nu!( cwd: "tests/fixtures/formats/", - format!("open sample-ps-output.json | select cpu mem virtual | sum | get {}", column_name) + format!("open sample-ps-output.json | select cpu mem virtual | math sum | get {}", column_name) ); let result = f64::from_str(&actual.out).map_err(|_| String::from("Failed to parse float."))?; @@ -108,7 +108,7 @@ fn compute_sum_of_table() -> Result<(), String> { fn sum_of_a_row_containing_a_table_is_an_error() { let actual = nu!( cwd: "tests/fixtures/formats/", - "open sample-sys-output.json | sum" + "open sample-sys-output.json | math sum" ); assert!(actual .err diff --git a/crates/nu-cli/tests/commands/where_.rs b/crates/nu-cli/tests/commands/where_.rs index 34082818..41a0a594 100644 --- a/crates/nu-cli/tests/commands/where_.rs +++ b/crates/nu-cli/tests/commands/where_.rs @@ -14,7 +14,7 @@ fn filters_by_unit_size_comparison() { fn filters_with_nothing_comparison() { let actual = nu!( cwd: "tests/fixtures/formats", - r#"echo '[{"foo": 3}, {"foo": null}, {"foo": 4}]' | from json | get foo | compact | where $it > 1 | sum | echo $it"# + r#"echo '[{"foo": 3}, {"foo": null}, {"foo": 4}]' | from json | get foo | compact | where $it > 1 | math sum | echo $it"# ); assert_eq!(actual.out, "7"); @@ -24,7 +24,7 @@ fn filters_with_nothing_comparison() { fn where_in_table() { let actual = nu!( cwd: "tests/fixtures/formats", - r#"echo '[{"name": "foo", "size": 3}, {"name": "foo", "size": 2}, {"name": "bar", "size": 4}]' | from json | where name in: ["foo"] | get size | sum | echo $it"# + r#"echo '[{"name": "foo", "size": 3}, {"name": "foo", "size": 2}, {"name": "bar", "size": 4}]' | from json | where name in: ["foo"] | get size | math sum | echo $it"# ); assert_eq!(actual.out, "5"); @@ -34,7 +34,7 @@ fn where_in_table() { fn where_not_in_table() { let actual = nu!( cwd: "tests/fixtures/formats", - r#"echo '[{"name": "foo", "size": 3}, {"name": "foo", "size": 2}, {"name": "bar", "size": 4}]' | from json | where name not-in: ["foo"] | get size | sum | echo $it"# + r#"echo '[{"name": "foo", "size": 3}, {"name": "foo", "size": 2}, {"name": "bar", "size": 4}]' | from json | where name not-in: ["foo"] | get size | math sum | echo $it"# ); assert_eq!(actual.out, "4"); diff --git a/crates/nu-test-support/src/lib.rs b/crates/nu-test-support/src/lib.rs index 8850dce0..a60b7521 100644 --- a/crates/nu-test-support/src/lib.rs +++ b/crates/nu-test-support/src/lib.rs @@ -36,14 +36,14 @@ mod tests { | from-csv | get rusty_luck | str --to-int - | sum + | math sum | echo "$it" "#, ); assert_eq!( actual, - r#"open los_tres_amigos.txt | from-csv | get rusty_luck | str --to-int | sum | echo "$it""# + r#"open los_tres_amigos.txt | from-csv | get rusty_luck | str --to-int | math sum | echo "$it""# ); } } diff --git a/docs/commands/math.md b/docs/commands/math.md index a9eba76a..0af856ef 100644 --- a/docs/commands/math.md +++ b/docs/commands/math.md @@ -1,95 +1,126 @@ # math + Mathematical functions that generally only operate on a list of numbers (integers, decimals, bytes) and tables. Currently the following functions are implemented: -`math average` Get the average of a list of number -`math min` Get the minimum of a list of numbers -`math max` Get the maximum of a list of numbers + +* `math average`: Finds the average of a list of numbers or tables +* `math min`: Finds the minimum within a list of numbers or tables +* `math max`: Finds the maximum within a list of numbers or tables +* `math sum`: Finds the sum of a list of numbers or tables However, the mathematical functions like `min` and `max` are more permissive and also work on `Dates`. ## Examples + To get the average of the file sizes in a directory, simply pipe the size column from the ls command to the average command. ### List of Numbers (Integers, Decimals, Bytes) + ```shell > ls - # │ name │ type │ size │ modified + # │ name │ type │ size │ modified ────┼────────────────────┼──────┼──────────┼───────────── - 0 │ CODE_OF_CONDUCT.md │ File │ 3.4 KB │ 4 days ago - 1 │ CONTRIBUTING.md │ File │ 1.3 KB │ 4 days ago - 2 │ Cargo.lock │ File │ 106.3 KB │ 6 mins ago - 3 │ Cargo.toml │ File │ 4.6 KB │ 3 days ago - 4 │ LICENSE │ File │ 1.1 KB │ 4 days ago - 5 │ Makefile.toml │ File │ 449 B │ 4 days ago - 6 │ README.md │ File │ 16.0 KB │ 6 mins ago - 7 │ TODO.md │ File │ 0 B │ 6 mins ago - 8 │ assets │ Dir │ 128 B │ 4 days ago - 9 │ build.rs │ File │ 78 B │ 4 days ago - 10 │ crates │ Dir │ 672 B │ 3 days ago - 11 │ debian │ Dir │ 352 B │ 4 days ago - 12 │ docker │ Dir │ 288 B │ 4 days ago - 13 │ docs │ Dir │ 160 B │ 4 days ago - 14 │ features.toml │ File │ 632 B │ 4 days ago - 15 │ images │ Dir │ 160 B │ 4 days ago - 16 │ justfile │ File │ 234 B │ 3 days ago - 17 │ rustfmt.toml │ File │ 16 B │ 4 days ago - 18 │ src │ Dir │ 128 B │ 4 days ago - 19 │ target │ Dir │ 192 B │ 8 hours ago - 20 │ tests │ Dir │ 192 B │ 4 days ago + 0 │ CODE_OF_CONDUCT.md │ File │ 3.4 KB │ 4 days ago + 1 │ CONTRIBUTING.md │ File │ 1.3 KB │ 4 days ago + 2 │ Cargo.lock │ File │ 106.3 KB │ 6 mins ago + 3 │ Cargo.toml │ File │ 4.6 KB │ 3 days ago + 4 │ LICENSE │ File │ 1.1 KB │ 4 days ago + 5 │ Makefile.toml │ File │ 449 B │ 4 days ago + 6 │ README.md │ File │ 16.0 KB │ 6 mins ago + 7 │ TODO.md │ File │ 0 B │ 6 mins ago + 8 │ assets │ Dir │ 128 B │ 4 days ago + 9 │ build.rs │ File │ 78 B │ 4 days ago + 10 │ crates │ Dir │ 672 B │ 3 days ago + 11 │ debian │ Dir │ 352 B │ 4 days ago + 12 │ docker │ Dir │ 288 B │ 4 days ago + 13 │ docs │ Dir │ 160 B │ 4 days ago + 14 │ features.toml │ File │ 632 B │ 4 days ago + 15 │ images │ Dir │ 160 B │ 4 days ago + 16 │ justfile │ File │ 234 B │ 3 days ago + 17 │ rustfmt.toml │ File │ 16 B │ 4 days ago + 18 │ src │ Dir │ 128 B │ 4 days ago + 19 │ target │ Dir │ 192 B │ 8 hours ago + 20 │ tests │ Dir │ 192 B │ 4 days ago +``` +```shell > ls | get size | math average ───┬──────── - 0 │ 6.5 KB + 0 │ 7.2 KB ───┴──────── +``` +```shell > ls | get size | math min ───┬───── - 0 │ 0 B + 0 │ 0 B ───┴───── +``` + +```shell > ls | get size | math max ───┬────────── - 0 │ 106.3 KB + 0 │ 113.5 KB ───┴────────── +``` -# Dates +```shell +> ls | get size | math sum +───┬────────── + 0 │ 143.4 KB +───┴────────── +``` + +### Dates + +```shell > ls | get modified | math min 2020-06-09 17:25:51.798743222 UTC +``` +```shell > ls | get modified | math max 2020-06-14 05:49:59.637449186 UT ``` ### Operations on tables -```shell -> pwd | split row / | size -───┬───────┬───────┬───────┬──────────── - # │ lines │ words │ chars │ max length -───┼───────┼───────┼───────┼──────────── - 0 │ 0 │ 1 │ 5 │ 5 - 1 │ 0 │ 1 │ 7 │ 7 - 2 │ 0 │ 1 │ 9 │ 9 - 3 │ 0 │ 1 │ 7 │ 7 -───┴───────┴───────┴───────┴──────────── +```shell +> pwd | split row / | size +───┬───────┬───────┬───────┬──────────── + # │ lines │ words │ chars │ max length +───┼───────┼───────┼───────┼──────────── + 0 │ 0 │ 1 │ 5 │ 5 + 1 │ 0 │ 1 │ 7 │ 7 + 2 │ 0 │ 1 │ 9 │ 9 + 3 │ 0 │ 1 │ 7 │ 7 +───┴───────┴───────┴───────┴──────────── +``` + +```shell > pwd | split row / | size | math max ───────────┬─── - lines │ 0 - words │ 1 - chars │ 9 - max length │ 9 + lines │ 0 + words │ 1 + chars │ 9 + max length │ 9 ────────────┴─── +``` +```shell > pwd | split row / | size | math average ────────────┬──────── - lines │ 0.0000 - words │ 1.0000 - chars │ 7.0000 - max length │ 7.0000 + lines │ 0.0000 + words │ 1.0000 + chars │ 7.0000 + max length │ 7.0000 ────────────┴──────── ``` ## Errors + `math` functions are aggregation functions so empty lists are invalid + ```shell > echo [] | math average error: Error: Unexpected: Cannot perform aggregate math operation on empty data @@ -102,7 +133,3 @@ then unexpected results can occur. > echo [1 2 a ] | math average 0 ``` - - - - diff --git a/docs/commands/str.md b/docs/commands/str.md index ce358043..f8eb32c4 100644 --- a/docs/commands/str.md +++ b/docs/commands/str.md @@ -41,7 +41,7 @@ Applies the subcommand to a value or a table. 1 │ │ filesystem │ ━━━┷━━━┷━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -> echo "1, 2, 3" | split row "," | str to-int | sum +> echo "1, 2, 3" | split row "," | str to-int | math sum ━━━━━━━━━ ───────── diff --git a/docs/commands/sum.md b/docs/commands/sum.md index e87dc65a..f36d5b26 100644 --- a/docs/commands/sum.md +++ b/docs/commands/sum.md @@ -5,7 +5,7 @@ This command allows you to calculate the sum of values in a column. To get the sum of the file sizes in a directory, simply pipe the size column from the ls command to the sum command. ```shell -> ls | get size | sum +> ls | get size | math sum ━━━━━━━━━ value ━━━━━━━━━ @@ -15,7 +15,7 @@ To get the sum of the file sizes in a directory, simply pipe the size column fro To get the sum of the characters that make up your present working directory. ```shell -> pwd | split-row / | size | get chars | sum +> pwd | split-row / | size | get chars | math sum ━━━━━━━━━ ━━━━━━━━━ @@ -27,15 +27,15 @@ Note that sum only works for integer and byte values. If the shell doesn't recog One way to solve this is to convert each row to an integer when possible and then pipe the result to `sum` ```shell -> open tests/fixtures/formats/caco3_plastics.csv | get tariff_item | sum +> open tests/fixtures/formats/caco3_plastics.csv | get tariff_item | math sum error: Unrecognized type in stream: Primitive(String("2509000000")) - shell:1:0 -1 | open tests/fixtures/formats/caco3_plastics.csv | get tariff_item | sum +1 | open tests/fixtures/formats/caco3_plastics.csv | get tariff_item | math sum | ^^^^ source ``` ```shell -> open tests/fixtures/formats/caco3_plastics.csv | get tariff_item | str --to-int | sum +> open tests/fixtures/formats/caco3_plastics.csv | get tariff_item | str --to-int | math sum ━━━━━━━━━━━━━ ───────────── diff --git a/tests/shell/pipeline/commands/internal.rs b/tests/shell/pipeline/commands/internal.rs index d5ebf7da..d30718cf 100644 --- a/tests/shell/pipeline/commands/internal.rs +++ b/tests/shell/pipeline/commands/internal.rs @@ -198,7 +198,7 @@ fn echoing_ranges() { let actual = nu!( cwd: ".", r#" - echo 1..3 | sum + echo 1..3 | math sum "# );