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
This commit is contained in:
Joseph T. Lyons 2020-06-18 22:02:01 -04:00 committed by GitHub
parent 5f9de80d9b
commit 53a6e9f0bd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 175 additions and 143 deletions

View File

@ -352,7 +352,7 @@ pub fn create_default_context(
whole_stream_command(MathMedian), whole_stream_command(MathMedian),
whole_stream_command(MathMinimum), whole_stream_command(MathMinimum),
whole_stream_command(MathMaximum), whole_stream_command(MathMaximum),
whole_stream_command(Sum), whole_stream_command(MathSummation),
// File format output // File format output
whole_stream_command(To), whole_stream_command(To),
whole_stream_command(ToBSON), whole_stream_command(ToBSON),

View File

@ -102,7 +102,6 @@ pub(crate) mod sort_by;
pub(crate) mod split; pub(crate) mod split;
pub(crate) mod split_by; pub(crate) mod split_by;
pub(crate) mod str_; pub(crate) mod str_;
pub(crate) mod sum;
#[allow(unused)] #[allow(unused)]
pub(crate) mod t_sort_by; pub(crate) mod t_sort_by;
pub(crate) mod table; pub(crate) mod table;
@ -199,7 +198,7 @@ pub(crate) use lines::Lines;
pub(crate) use ls::Ls; pub(crate) use ls::Ls;
#[allow(unused_imports)] #[allow(unused_imports)]
pub(crate) use map_max_by::MapMaxBy; 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 merge::Merge;
pub(crate) use mkdir::Mkdir; pub(crate) use mkdir::Mkdir;
pub(crate) use mv::Move; pub(crate) use mv::Move;
@ -236,7 +235,6 @@ pub(crate) use str_::{
Str, StrCapitalize, StrDowncase, StrFindReplace, StrSet, StrSubstring, StrToDatetime, Str, StrCapitalize, StrDowncase, StrFindReplace, StrSet, StrSubstring, StrToDatetime,
StrToDecimal, StrToInteger, StrTrim, StrUpcase, StrToDecimal, StrToInteger, StrTrim, StrUpcase,
}; };
pub(crate) use sum::Sum;
#[allow(unused_imports)] #[allow(unused_imports)]
pub(crate) use t_sort_by::TSortBy; pub(crate) use t_sort_by::TSortBy;
pub(crate) use table::Table; pub(crate) use table::Table;

View File

@ -56,7 +56,7 @@ impl WholeStreamCommand for Each {
}, },
Example { Example {
description: "Echo the sum of each row", 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![ result: Some(vec![
UntaggedValue::int(3).into(), UntaggedValue::int(3).into(),
UntaggedValue::int(7).into(), UntaggedValue::int(7).into(),

View File

@ -22,7 +22,7 @@ impl WholeStreamCommand for SubCommand {
} }
fn usage(&self) -> &str { 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( async fn run(
@ -56,7 +56,7 @@ impl WholeStreamCommand for SubCommand {
} }
pub fn average(values: &[Value], name: &Tag) -> Result<Value, ShellError> { pub fn average(values: &[Value], name: &Tag) -> Result<Value, ShellError> {
let sum = reducer_for(Reduce::Sum); let sum = reducer_for(Reduce::Summation);
let number = BigDecimal::from_usize(values.len()).ok_or_else(|| { let number = BigDecimal::from_usize(values.len()).ok_or_else(|| {
ShellError::labeled_error( ShellError::labeled_error(

View File

@ -35,7 +35,7 @@ impl WholeStreamCommand for Command {
mod tests { mod tests {
use super::*; use super::*;
use crate::commands::math::{ 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_plugin::test_helpers::value::{decimal, int};
use nu_protocol::Value; use nu_protocol::Value;
@ -67,31 +67,41 @@ mod tests {
description: "Single value", description: "Single value",
values: vec![int(10)], values: vec![int(10)],
expected_err: None, 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 { TestCase {
description: "Multiple Values", description: "Multiple Values",
values: vec![int(10), int(30), int(20)], values: vec![int(10), int(30), int(20)],
expected_err: None, 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 { TestCase {
description: "Mixed Values", description: "Mixed Values",
values: vec![int(10), decimal(26.5), decimal(26.5)], values: vec![int(10), decimal(26.5), decimal(26.5)],
expected_err: None, 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 { TestCase {
description: "Negative Values", description: "Negative Values",
values: vec![int(10), int(-11), int(-14)], values: vec![int(10), int(-11), int(-14)],
expected_err: None, 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 { TestCase {
description: "Mixed Negative Values", description: "Mixed Negative Values",
values: vec![int(10), decimal(-11.5), decimal(-13.5)], values: vec![int(10), decimal(-11.5), decimal(-13.5)],
expected_err: None, 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 // TODO-Uncomment once I figure out how to structure tables
// TestCase { // TestCase {
@ -116,7 +126,7 @@ mod tests {
for tc in tt.iter() { for tc in tt.iter() {
let tc: &TestCase = tc; // Just for type annotations let tc: &TestCase = tc; // Just for type annotations
let math_functions: Vec<MathFunction> = vec![average, minimum, maximum]; let math_functions: Vec<MathFunction> = vec![average, minimum, maximum, summation];
let results = math_functions let results = math_functions
.iter() .iter()
.map(|mf| mf(&tc.values, &test_tag)) .map(|mf| mf(&tc.values, &test_tag))

View File

@ -18,7 +18,7 @@ impl WholeStreamCommand for SubCommand {
} }
fn usage(&self) -> &str { 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( async fn run(

View File

@ -121,7 +121,7 @@ pub fn median(values: &[Value], name: &Tag) -> Result<Value, ShellError> {
fn compute_average(values: &[Value], name: impl Into<Tag>) -> Result<Value, ShellError> { fn compute_average(values: &[Value], name: impl Into<Tag>) -> Result<Value, ShellError> {
let name = name.into(); 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(|| { let number = BigDecimal::from_usize(2).ok_or_else(|| {
ShellError::labeled_error( ShellError::labeled_error(
"could not convert to big decimal", "could not convert to big decimal",

View File

@ -3,6 +3,7 @@ pub mod command;
pub mod max; pub mod max;
pub mod median; pub mod median;
pub mod min; pub mod min;
pub mod sum;
pub mod utils; pub mod utils;
pub use average::SubCommand as MathAverage; pub use average::SubCommand as MathAverage;
@ -10,3 +11,4 @@ pub use command::Command as Math;
pub use max::SubCommand as MathMaximum; pub use max::SubCommand as MathMaximum;
pub use median::SubCommand as MathMedian; pub use median::SubCommand as MathMedian;
pub use min::SubCommand as MathMinimum; pub use min::SubCommand as MathMinimum;
pub use sum::SubCommand as MathSummation;

View File

@ -1,26 +1,25 @@
use crate::commands::math::utils::calculate;
use crate::commands::WholeStreamCommand; use crate::commands::WholeStreamCommand;
use crate::prelude::*; use crate::prelude::*;
use crate::utils::data_processing::{reducer_for, Reduce}; use crate::utils::data_processing::{reducer_for, Reduce};
use nu_errors::ShellError; 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 num_traits::identities::Zero;
use indexmap::map::IndexMap; pub struct SubCommand;
pub struct Sum;
#[async_trait] #[async_trait]
impl WholeStreamCommand for Sum { impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str { fn name(&self) -> &str {
"sum" "math sum"
} }
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("sum") Signature::build("math sum")
} }
fn usage(&self) -> &str { fn usage(&self) -> &str {
"Sums the values." "Finds the sum of a list of numbers or tables"
} }
async fn run( async fn run(
@ -28,16 +27,19 @@ impl WholeStreamCommand for Sum {
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
sum(RunnableContext { calculate(
input: args.input, RunnableContext {
registry: registry.clone(), input: args.input,
shell_manager: args.shell_manager, registry: registry.clone(),
host: args.host, shell_manager: args.shell_manager,
ctrl_c: args.ctrl_c, host: args.host,
current_errors: args.current_errors, ctrl_c: args.ctrl_c,
name: args.call_info.name_tag, current_errors: args.current_errors,
raw_input: args.raw_input, name: args.call_info.name_tag,
}) raw_input: args.raw_input,
},
summation,
)
.await .await
} }
@ -45,31 +47,28 @@ impl WholeStreamCommand for Sum {
vec![ vec![
Example { Example {
description: "Sum a list of numbers", 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()]), result: Some(vec![UntaggedValue::int(6).into()]),
}, },
Example { Example {
description: "Get the disk usage for the current directory", 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, result: None,
}, },
] ]
} }
} }
async fn sum( pub fn summation(values: &[Value], name: &Tag) -> Result<Value, ShellError> {
RunnableContext { mut input, .. }: RunnableContext, let sum = reducer_for(Reduce::Summation);
) -> Result<OutputStream, ShellError> {
let values: Vec<Value> = input.drain_vec().await;
let action = reducer_for(Reduce::Sum);
if values.iter().all(|v| v.is_primitive()) { if values.iter().all(|v| v.is_primitive()) {
let total = action(Value::zero(), values)?; Ok(sum(Value::zero(), values.to_vec())?)
Ok(OutputStream::one(ReturnSuccess::value(total)))
} else { } else {
let mut column_values = IndexMap::new(); let mut column_values = IndexMap::new();
for value in values { 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() { for (key, value) in row_dict.entries.iter() {
column_values column_values
.entry(key.clone()) .entry(key.clone())
@ -80,32 +79,28 @@ async fn sum(
} }
let mut column_totals = IndexMap::new(); let mut column_totals = IndexMap::new();
for (col_name, col_vals) in column_values { for (col_name, col_vals) in column_values {
let sum = action(Value::zero(), col_vals); let sum = sum(Value::zero(), col_vals)?;
match sum {
Ok(value) => { column_totals.insert(col_name, sum);
column_totals.insert(col_name, value);
}
Err(err) => return Err(err),
};
} }
Ok(OutputStream::one(ReturnSuccess::value(
UntaggedValue::Row(Dictionary { Ok(UntaggedValue::Row(Dictionary {
entries: column_totals, entries: column_totals,
}) })
.into_untagged_value(), .into_value(name))
)))
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::Sum; use super::SubCommand;
#[test] #[test]
fn examples_work_as_expected() { fn examples_work_as_expected() {
use crate::examples::test as test_examples; use crate::examples::test as test_examples;
test_examples(Sum {}) test_examples(SubCommand {})
} }
} }

View File

@ -288,14 +288,14 @@ pub fn reducer_for(
command: Reduce, command: Reduce,
) -> Box<dyn Fn(Value, Vec<Value>) -> Result<Value, ShellError> + Send + Sync + 'static> { ) -> Box<dyn Fn(Value, Vec<Value>) -> Result<Value, ShellError> + Send + Sync + 'static> {
match command { 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::Minimum => Box::new(|_, values| min(values)),
Reduce::Maximum => Box::new(|_, values| max(values)), Reduce::Maximum => Box::new(|_, values| max(values)),
} }
} }
pub enum Reduce { pub enum Reduce {
Sum, Summation,
Minimum, Minimum,
Maximum, Maximum,
Default, Default,
@ -309,7 +309,7 @@ pub fn reduce(
let tag = tag.into(); let tag = tag.into();
let reduce_with = match reducer { 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 == "min" => reducer_for(Reduce::Minimum),
Some(cmd) if cmd == "max" => reducer_for(Reduce::Maximum), Some(cmd) if cmd == "max" => reducer_for(Reduce::Maximum),
Some(_) | None => reducer_for(Reduce::Default), Some(_) | None => reducer_for(Reduce::Default),
@ -642,7 +642,7 @@ mod tests {
fn reducer_computes_given_a_sum_command() -> Result<(), ShellError> { fn reducer_computes_given_a_sum_command() -> Result<(), ShellError> {
let subject = vec![int(1), int(1), int(1)]; 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)); assert_eq!(action(Value::zero(), subject)?, int(3));

View File

@ -4,7 +4,7 @@ use nu_test_support::nu;
fn drop_rows() { fn drop_rows() {
let actual = nu!( let actual = nu!(
cwd: "tests/fixtures/formats", 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"); assert_eq!(actual.out, "3");

View File

@ -5,7 +5,7 @@ fn each_works_separately() {
let actual = nu!( let actual = nu!(
cwd: "tests/fixtures/formats", pipeline( cwd: "tests/fixtures/formats", pipeline(
r#" 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
"# "#
)); ));

View File

@ -22,7 +22,7 @@ fn adds_value_provided_if_column_is_empty() {
open likes.csv open likes.csv
| empty? likes 1 | empty? likes 1
| get likes | get likes
| sum | math sum
| echo $it | echo $it
"# "#
)); ));
@ -43,7 +43,7 @@ fn adds_value_provided_for_columns_that_are_empty() {
{"boost": 1, "check": {}}, {"boost": 1, "check": {}},
{"boost": null, "check": ["" {} [] ""]} {"boost": null, "check": ["" {} [] ""]}
] ]
"#, "#,
)]); )]);
@ -53,7 +53,7 @@ fn adds_value_provided_for_columns_that_are_empty() {
open checks.json open checks.json
| empty? boost check 1 | empty? boost check 1
| get boost check | get boost check
| sum | math sum
| echo $it | echo $it
"# "#
)); ));

View File

@ -22,7 +22,7 @@ fn rows() {
open caballeros.csv open caballeros.csv
| keep 3 | keep 3
| get lucky_code | get lucky_code
| sum | math sum
| echo $it | echo $it
"# "#
)); ));

View File

@ -41,7 +41,7 @@ fn condition_is_met() {
| keep-until "Chicken Collection" == "Red Chickens" | keep-until "Chicken Collection" == "Red Chickens"
| str to-int "31/04/2020" | str to-int "31/04/2020"
| get "31/04/2020" | get "31/04/2020"
| sum | math sum
| echo $it | echo $it
"# "#
)); ));

View File

@ -41,7 +41,7 @@ fn condition_is_met() {
| keep-while "Chicken Collection" != "Blue Chickens" | keep-while "Chicken Collection" != "Blue Chickens"
| str to-int "31/04/2020" | str to-int "31/04/2020"
| get "31/04/2020" | get "31/04/2020"
| sum | math sum
| echo $it | echo $it
"# "#
)); ));

View File

@ -33,7 +33,7 @@ fn row() {
| merge { open new_caballeros.csv } | merge { open new_caballeros.csv }
| where country in: ["Guayaquil Ecuador" "New Zealand"] | where country in: ["Guayaquil Ecuador" "New Zealand"]
| get luck | get luck
| sum | math sum
| echo $it | echo $it
"# "#
)); ));

View File

@ -40,7 +40,7 @@ fn condition_is_met() {
| skip-until "Chicken Collection" == "Red Chickens" | skip-until "Chicken Collection" == "Red Chickens"
| str to-int "31/04/2020" | str to-int "31/04/2020"
| get "31/04/2020" | get "31/04/2020"
| sum | math sum
| echo $it | echo $it
"# "#
)); ));

View File

@ -108,7 +108,7 @@ fn converts_to_decimal() {
echo "3.1, 0.0415" echo "3.1, 0.0415"
| split row "," | split row ","
| str to-decimal | str to-decimal
| sum | math sum
"# "#
)); ));
@ -130,7 +130,7 @@ fn sets() {
cwd: dirs.test(), pipeline( cwd: dirs.test(), pipeline(
r#" r#"
open sample.toml open sample.toml
| str set wykittenshell package.name | str set wykittenshell package.name
| get package.name | get package.name
| echo $it | echo $it
"# "#

View File

@ -25,7 +25,7 @@ fn all() {
open meals.json open meals.json
| get meals | get meals
| get calories | get calories
| sum | math sum
| echo $it | echo $it
"# "#
)); ));
@ -53,7 +53,7 @@ fn outputs_zero_with_no_input() {
let actual = nu!( let actual = nu!(
cwd: dirs.test(), pipeline( cwd: dirs.test(), pipeline(
r#" r#"
sum math sum
| echo $it | echo $it
"# "#
)); ));
@ -74,7 +74,7 @@ fn compute_sum_of_individual_row() -> Result<(), String> {
for (column_name, expected_value) in answers_for_columns.iter() { for (column_name, expected_value) in answers_for_columns.iter() {
let actual = nu!( let actual = nu!(
cwd: "tests/fixtures/formats/", 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 = let result =
f64::from_str(&actual.out).map_err(|_| String::from("Failed to parse float."))?; 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() { for (column_name, expected_value) in answers_for_columns.iter() {
let actual = nu!( let actual = nu!(
cwd: "tests/fixtures/formats/", 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 = let result =
f64::from_str(&actual.out).map_err(|_| String::from("Failed to parse float."))?; 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() { fn sum_of_a_row_containing_a_table_is_an_error() {
let actual = nu!( let actual = nu!(
cwd: "tests/fixtures/formats/", cwd: "tests/fixtures/formats/",
"open sample-sys-output.json | sum" "open sample-sys-output.json | math sum"
); );
assert!(actual assert!(actual
.err .err

View File

@ -14,7 +14,7 @@ fn filters_by_unit_size_comparison() {
fn filters_with_nothing_comparison() { fn filters_with_nothing_comparison() {
let actual = nu!( let actual = nu!(
cwd: "tests/fixtures/formats", 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"); assert_eq!(actual.out, "7");
@ -24,7 +24,7 @@ fn filters_with_nothing_comparison() {
fn where_in_table() { fn where_in_table() {
let actual = nu!( let actual = nu!(
cwd: "tests/fixtures/formats", 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"); assert_eq!(actual.out, "5");
@ -34,7 +34,7 @@ fn where_in_table() {
fn where_not_in_table() { fn where_not_in_table() {
let actual = nu!( let actual = nu!(
cwd: "tests/fixtures/formats", 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"); assert_eq!(actual.out, "4");

View File

@ -36,14 +36,14 @@ mod tests {
| from-csv | from-csv
| get rusty_luck | get rusty_luck
| str --to-int | str --to-int
| sum | math sum
| echo "$it" | echo "$it"
"#, "#,
); );
assert_eq!( assert_eq!(
actual, 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""#
); );
} }
} }

View File

@ -1,95 +1,126 @@
# math # math
Mathematical functions that generally only operate on a list of numbers (integers, decimals, bytes) and tables. Mathematical functions that generally only operate on a list of numbers (integers, decimals, bytes) and tables.
Currently the following functions are implemented: 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 average`: Finds the average of a list of numbers or tables
`math max` Get the maximum of a list of numbers * `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`. However, the mathematical functions like `min` and `max` are more permissive and also work on `Dates`.
## Examples ## 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. 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) ### List of Numbers (Integers, Decimals, Bytes)
```shell ```shell
> ls > ls
# │ name │ type │ size │ modified # │ name │ type │ size │ modified
────┼────────────────────┼──────┼──────────┼───────────── ────┼────────────────────┼──────┼──────────┼─────────────
0 │ CODE_OF_CONDUCT.md │ File │ 3.4 KB │ 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 1 │ CONTRIBUTING.md │ File │ 1.3 KB │ 4 days ago
2 │ Cargo.lock │ File │ 106.3 KB │ 6 mins ago 2 │ Cargo.lock │ File │ 106.3 KB │ 6 mins ago
3 │ Cargo.toml │ File │ 4.6 KB │ 3 days ago 3 │ Cargo.toml │ File │ 4.6 KB │ 3 days ago
4 │ LICENSE │ File │ 1.1 KB │ 4 days ago 4 │ LICENSE │ File │ 1.1 KB │ 4 days ago
5 │ Makefile.toml │ File │ 449 B │ 4 days ago 5 │ Makefile.toml │ File │ 449 B │ 4 days ago
6 │ README.md │ File │ 16.0 KB │ 6 mins ago 6 │ README.md │ File │ 16.0 KB │ 6 mins ago
7 │ TODO.md │ File │ 0 B │ 6 mins ago 7 │ TODO.md │ File │ 0 B │ 6 mins ago
8 │ assets │ Dir │ 128 B │ 4 days ago 8 │ assets │ Dir │ 128 B │ 4 days ago
9 │ build.rs │ File │ 78 B │ 4 days ago 9 │ build.rs │ File │ 78 B │ 4 days ago
10 │ crates │ Dir │ 672 B │ 3 days ago 10 │ crates │ Dir │ 672 B │ 3 days ago
11 │ debian │ Dir │ 352 B │ 4 days ago 11 │ debian │ Dir │ 352 B │ 4 days ago
12 │ docker │ Dir │ 288 B │ 4 days ago 12 │ docker │ Dir │ 288 B │ 4 days ago
13 │ docs │ Dir │ 160 B │ 4 days ago 13 │ docs │ Dir │ 160 B │ 4 days ago
14 │ features.toml │ File │ 632 B │ 4 days ago 14 │ features.toml │ File │ 632 B │ 4 days ago
15 │ images │ Dir │ 160 B │ 4 days ago 15 │ images │ Dir │ 160 B │ 4 days ago
16 │ justfile │ File │ 234 B │ 3 days ago 16 │ justfile │ File │ 234 B │ 3 days ago
17 │ rustfmt.toml │ File │ 16 B │ 4 days ago 17 │ rustfmt.toml │ File │ 16 B │ 4 days ago
18 │ src │ Dir │ 128 B │ 4 days ago 18 │ src │ Dir │ 128 B │ 4 days ago
19 │ target │ Dir │ 192 B │ 8 hours ago 19 │ target │ Dir │ 192 B │ 8 hours ago
20 │ tests │ Dir │ 192 B │ 4 days ago 20 │ tests │ Dir │ 192 B │ 4 days ago
```
```shell
> ls | get size | math average > ls | get size | math average
───┬──────── ───┬────────
0 │ 6.5 KB 0 │ 7.2 KB
───┴──────── ───┴────────
```
```shell
> ls | get size | math min > ls | get size | math min
───┬───── ───┬─────
0 │ 0 B 0 │ 0 B
───┴───── ───┴─────
```
```shell
> ls | get size | math max > 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 > ls | get modified | math min
2020-06-09 17:25:51.798743222 UTC 2020-06-09 17:25:51.798743222 UTC
```
```shell
> ls | get modified | math max > ls | get modified | math max
2020-06-14 05:49:59.637449186 UT 2020-06-14 05:49:59.637449186 UT
``` ```
### Operations on tables ### 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 > pwd | split row / | size | math max
───────────┬─── ───────────┬───
lines │ 0 lines │ 0
words │ 1 words │ 1
chars │ 9 chars │ 9
max length │ 9 max length │ 9
────────────┴─── ────────────┴───
```
```shell
> pwd | split row / | size | math average > pwd | split row / | size | math average
────────────┬──────── ────────────┬────────
lines │ 0.0000 lines │ 0.0000
words │ 1.0000 words │ 1.0000
chars │ 7.0000 chars │ 7.0000
max length │ 7.0000 max length │ 7.0000
────────────┴──────── ────────────┴────────
``` ```
## Errors ## Errors
`math` functions are aggregation functions so empty lists are invalid `math` functions are aggregation functions so empty lists are invalid
```shell ```shell
> echo [] | math average > echo [] | math average
error: Error: Unexpected: Cannot perform aggregate math operation on empty data 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 > echo [1 2 a ] | math average
0 0
``` ```

View File

@ -41,7 +41,7 @@ Applies the subcommand to a value or a table.
1 │ │ filesystem │ 1 │ │ filesystem │
━━━┷━━━┷━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ━━━┷━━━┷━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
> echo "1, 2, 3" | split row "," | str to-int | sum > echo "1, 2, 3" | split row "," | str to-int | math sum
━━━━━━━━━ ━━━━━━━━━
<value> <value>
───────── ─────────

View File

@ -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. 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 ```shell
> ls | get size | sum > ls | get size | math sum
━━━━━━━━━ ━━━━━━━━━
value 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. To get the sum of the characters that make up your present working directory.
```shell ```shell
> pwd | split-row / | size | get chars | sum > pwd | split-row / | size | get chars | math sum
━━━━━━━━━ ━━━━━━━━━
<value> <value>
━━━━━━━━━ ━━━━━━━━━
@ -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` One way to solve this is to convert each row to an integer when possible and then pipe the result to `sum`
```shell ```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")) error: Unrecognized type in stream: Primitive(String("2509000000"))
- shell:1:0 - 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 | ^^^^ source
``` ```
```shell ```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
━━━━━━━━━━━━━ ━━━━━━━━━━━━━
<value> <value>
───────────── ─────────────

View File

@ -198,7 +198,7 @@ fn echoing_ranges() {
let actual = nu!( let actual = nu!(
cwd: ".", cwd: ".",
r#" r#"
echo 1..3 | sum echo 1..3 | math sum
"# "#
); );