mirror of
https://github.com/nushell/nushell.git
synced 2025-01-22 22:29:10 +01:00
Add rounding functionalties (#2672)
* added math round * added math floor * added math ceil * added math.md examples * moved the detection of nonnumerical values in ceil/floor/round * math round now works on streams * math floor now works on streams * math ceil now works on streams
This commit is contained in:
parent
cc06ea4d87
commit
0d8064ed2d
@ -223,6 +223,9 @@ pub fn create_default_context(interactive: bool) -> Result<EvaluationContext, Bo
|
||||
whole_stream_command(MathSummation),
|
||||
whole_stream_command(MathVariance),
|
||||
whole_stream_command(MathProduct),
|
||||
whole_stream_command(MathRound),
|
||||
whole_stream_command(MathFloor),
|
||||
whole_stream_command(MathCeil),
|
||||
// File format output
|
||||
whole_stream_command(To),
|
||||
whole_stream_command(ToCSV),
|
||||
|
@ -208,8 +208,8 @@ pub(crate) use last::Last;
|
||||
pub(crate) use lines::Lines;
|
||||
pub(crate) use ls::Ls;
|
||||
pub(crate) use math::{
|
||||
Math, MathAverage, MathEval, MathMaximum, MathMedian, MathMinimum, MathMode, MathProduct,
|
||||
MathStddev, MathSummation, MathVariance,
|
||||
Math, MathAverage, MathCeil, MathEval, MathFloor, MathMaximum, MathMedian, MathMinimum,
|
||||
MathMode, MathProduct, MathRound, MathStddev, MathSummation, MathVariance,
|
||||
};
|
||||
pub(crate) use merge::Merge;
|
||||
pub(crate) use mkdir::Mkdir;
|
||||
|
91
crates/nu-cli/src/commands/math/ceil.rs
Normal file
91
crates/nu-cli/src/commands/math/ceil.rs
Normal file
@ -0,0 +1,91 @@
|
||||
use crate::commands::math::utils::run_with_numerical_functions_on_stream;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use bigdecimal::One;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Signature, UntaggedValue, Value};
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"math ceil"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("math celi")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Applies the ceil function to a list of numbers"
|
||||
}
|
||||
|
||||
async fn run(
|
||||
&self,
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
run_with_numerical_functions_on_stream(
|
||||
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,
|
||||
},
|
||||
ceil_big_int,
|
||||
ceil_big_decimal,
|
||||
ceil_default,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Apply the ceil function to a list of numbers",
|
||||
example: "echo [1.5 2.3 -3.1] | math ceil",
|
||||
result: Some(vec![
|
||||
UntaggedValue::int(2).into(),
|
||||
UntaggedValue::int(3).into(),
|
||||
UntaggedValue::int(-3).into(),
|
||||
]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn ceil_big_int(val: BigInt) -> Value {
|
||||
UntaggedValue::int(val).into()
|
||||
}
|
||||
|
||||
fn ceil_big_decimal(val: BigDecimal) -> Value {
|
||||
let mut maybe_ceiled = val.round(0);
|
||||
if maybe_ceiled < val {
|
||||
maybe_ceiled += BigDecimal::one();
|
||||
}
|
||||
let (ceiled, _) = maybe_ceiled.into_bigint_and_exponent();
|
||||
UntaggedValue::int(ceiled).into()
|
||||
}
|
||||
|
||||
fn ceil_default(_: UntaggedValue) -> Value {
|
||||
UntaggedValue::Error(ShellError::unexpected(
|
||||
"Only numerical values are supported",
|
||||
))
|
||||
.into()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::ShellError;
|
||||
use super::SubCommand;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
Ok(test_examples(SubCommand {})?)
|
||||
}
|
||||
}
|
91
crates/nu-cli/src/commands/math/floor.rs
Normal file
91
crates/nu-cli/src/commands/math/floor.rs
Normal file
@ -0,0 +1,91 @@
|
||||
use crate::commands::math::utils::run_with_numerical_functions_on_stream;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use bigdecimal::One;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Signature, UntaggedValue, Value};
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"math floor"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("math floor")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Applies the floor function to a list of numbers"
|
||||
}
|
||||
|
||||
async fn run(
|
||||
&self,
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
run_with_numerical_functions_on_stream(
|
||||
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,
|
||||
},
|
||||
floor_big_int,
|
||||
floor_big_decimal,
|
||||
floor_default,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Apply the floor function to a list of numbers",
|
||||
example: "echo [1.5 2.3 -3.1] | math floor",
|
||||
result: Some(vec![
|
||||
UntaggedValue::int(1).into(),
|
||||
UntaggedValue::int(2).into(),
|
||||
UntaggedValue::int(-4).into(),
|
||||
]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn floor_big_int(val: BigInt) -> Value {
|
||||
UntaggedValue::int(val).into()
|
||||
}
|
||||
|
||||
fn floor_big_decimal(val: BigDecimal) -> Value {
|
||||
let mut maybe_floored = val.round(0);
|
||||
if maybe_floored > val {
|
||||
maybe_floored -= BigDecimal::one();
|
||||
}
|
||||
let (floored, _) = maybe_floored.into_bigint_and_exponent();
|
||||
UntaggedValue::int(floored).into()
|
||||
}
|
||||
|
||||
fn floor_default(_: UntaggedValue) -> Value {
|
||||
UntaggedValue::Error(ShellError::unexpected(
|
||||
"Only numerical values are supported",
|
||||
))
|
||||
.into()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::ShellError;
|
||||
use super::SubCommand;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
Ok(test_examples(SubCommand {})?)
|
||||
}
|
||||
}
|
@ -1,11 +1,14 @@
|
||||
pub mod avg;
|
||||
pub mod ceil;
|
||||
pub mod command;
|
||||
pub mod eval;
|
||||
pub mod floor;
|
||||
pub mod max;
|
||||
pub mod median;
|
||||
pub mod min;
|
||||
pub mod mode;
|
||||
pub mod product;
|
||||
pub mod round;
|
||||
pub mod stddev;
|
||||
pub mod sum;
|
||||
pub mod variance;
|
||||
@ -14,13 +17,16 @@ mod reducers;
|
||||
mod utils;
|
||||
|
||||
pub use avg::SubCommand as MathAverage;
|
||||
pub use ceil::SubCommand as MathCeil;
|
||||
pub use command::Command as Math;
|
||||
pub use eval::SubCommand as MathEval;
|
||||
pub use floor::SubCommand as MathFloor;
|
||||
pub use max::SubCommand as MathMaximum;
|
||||
pub use median::SubCommand as MathMedian;
|
||||
pub use min::SubCommand as MathMinimum;
|
||||
pub use mode::SubCommand as MathMode;
|
||||
pub use product::SubCommand as MathProduct;
|
||||
pub use round::SubCommand as MathRound;
|
||||
pub use stddev::SubCommand as MathStddev;
|
||||
pub use sum::SubCommand as MathSummation;
|
||||
pub use variance::SubCommand as MathVariance;
|
||||
|
85
crates/nu-cli/src/commands/math/round.rs
Normal file
85
crates/nu-cli/src/commands/math/round.rs
Normal file
@ -0,0 +1,85 @@
|
||||
use crate::commands::math::utils::run_with_numerical_functions_on_stream;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Signature, UntaggedValue, Value};
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"math round"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("math round")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Applies the round function to a list of numbers"
|
||||
}
|
||||
|
||||
async fn run(
|
||||
&self,
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
run_with_numerical_functions_on_stream(
|
||||
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,
|
||||
},
|
||||
round_big_int,
|
||||
round_big_decimal,
|
||||
round_default,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Apply the round function to a list of numbers",
|
||||
example: "echo [1.5 2.3 -3.1] | math round",
|
||||
result: Some(vec![
|
||||
UntaggedValue::int(2).into(),
|
||||
UntaggedValue::int(2).into(),
|
||||
UntaggedValue::int(-3).into(),
|
||||
]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn round_big_int(val: BigInt) -> Value {
|
||||
UntaggedValue::int(val).into()
|
||||
}
|
||||
|
||||
fn round_big_decimal(val: BigDecimal) -> Value {
|
||||
let (rounded, _) = val.round(0).as_bigint_and_exponent();
|
||||
UntaggedValue::int(rounded).into()
|
||||
}
|
||||
|
||||
fn round_default(_: UntaggedValue) -> Value {
|
||||
UntaggedValue::Error(ShellError::unexpected(
|
||||
"Only numerical values are supported",
|
||||
))
|
||||
.into()
|
||||
}
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::ShellError;
|
||||
use super::SubCommand;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
Ok(test_examples(SubCommand {})?)
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Dictionary, ReturnSuccess, UntaggedValue, Value};
|
||||
use nu_protocol::{Dictionary, Primitive, ReturnSuccess, UntaggedValue, Value};
|
||||
|
||||
use indexmap::map::IndexMap;
|
||||
|
||||
@ -31,6 +31,26 @@ pub async fn run_with_function(
|
||||
}
|
||||
}
|
||||
|
||||
pub type IntFunction = fn(val: BigInt) -> Value;
|
||||
|
||||
pub type DecimalFunction = fn(val: BigDecimal) -> Value;
|
||||
|
||||
pub type DefaultFunction = fn(val: UntaggedValue) -> Value;
|
||||
|
||||
pub async fn run_with_numerical_functions_on_stream(
|
||||
RunnableContext { input, .. }: RunnableContext,
|
||||
int_function: IntFunction,
|
||||
decimal_function: DecimalFunction,
|
||||
default_function: DefaultFunction,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let mapped = input.map(move |val| match val.value {
|
||||
UntaggedValue::Primitive(Primitive::Int(val)) => int_function(val),
|
||||
UntaggedValue::Primitive(Primitive::Decimal(val)) => decimal_function(val),
|
||||
other => default_function(other),
|
||||
});
|
||||
Ok(OutputStream::from_input(mapped))
|
||||
}
|
||||
|
||||
pub fn calculate(values: &[Value], name: &Tag, mf: MathFunction) -> Result<Value, ShellError> {
|
||||
if values.iter().all(|v| v.is_primitive()) {
|
||||
mf(&values, &name)
|
||||
|
@ -4,11 +4,14 @@ Mathematical functions that generally only operate on a list of numbers (integer
|
||||
Currently the following functions are implemented:
|
||||
|
||||
* `math avg`: Finds the average of a list of numbers or tables
|
||||
* `math ceil`: Applies the ceil function to a list of numbers
|
||||
* [`math eval`](math-eval.md): Evaluates a list of math expressions into numbers
|
||||
* `math floor`: Applies the floor function to a list of numbers
|
||||
* `math max`: Finds the maximum within a list of numbers or tables
|
||||
* `math median`: Finds the median of a list of numbers or tables
|
||||
* `math min`: Finds the minimum within a list of numbers or tables
|
||||
* `math mode`: Finds the most frequent element(s) within a list of numbers or tables
|
||||
* `math round`: Applies the round function to a list of numbers
|
||||
* `math stddev`: Finds the standard deviation of a list of numbers or tables
|
||||
* `math sum`: Finds the sum of a list of numbers or tables
|
||||
* `math product`: Finds the product of a list of numbers or tables
|
||||
@ -117,6 +120,33 @@ To get the average of the file sizes in a directory, simply pipe the size column
|
||||
328.96
|
||||
```
|
||||
|
||||
```shell
|
||||
> echo [1.5 2.3 -3.1] | math ceil
|
||||
───┬────
|
||||
0 │ 2
|
||||
1 │ 3
|
||||
2 │ -3
|
||||
───┴────
|
||||
```
|
||||
|
||||
```shell
|
||||
> echo [1.5 2.3 -3.1] | math floor
|
||||
───┬────
|
||||
0 │ 1
|
||||
1 │ 2
|
||||
2 │ -4
|
||||
───┴────
|
||||
```
|
||||
|
||||
```shell
|
||||
> echo [1.5 2.3 -3.1] | math round
|
||||
───┬────
|
||||
0 │ 2
|
||||
1 │ 2
|
||||
2 │ -3
|
||||
───┴────
|
||||
```
|
||||
|
||||
### Dates
|
||||
|
||||
```shell
|
||||
|
Loading…
Reference in New Issue
Block a user