mirror of
https://github.com/nushell/nushell.git
synced 2025-08-09 18:47:45 +02:00
Make sum plugin as internal command. (#1501)
This commit is contained in:
committed by
GitHub
parent
390deb4ff7
commit
21a543a901
@ -313,6 +313,7 @@ pub fn create_default_context(
|
||||
whole_stream_command(Pivot),
|
||||
// Data processing
|
||||
whole_stream_command(Histogram),
|
||||
whole_stream_command(Sum),
|
||||
// File format output
|
||||
whole_stream_command(ToBSON),
|
||||
whole_stream_command(ToCSV),
|
||||
|
@ -81,6 +81,7 @@ pub(crate) mod sort_by;
|
||||
pub(crate) mod split_by;
|
||||
pub(crate) mod split_column;
|
||||
pub(crate) mod split_row;
|
||||
pub(crate) mod sum;
|
||||
#[allow(unused)]
|
||||
pub(crate) mod t_sort_by;
|
||||
pub(crate) mod table;
|
||||
@ -186,6 +187,7 @@ pub(crate) use sort_by::SortBy;
|
||||
pub(crate) use split_by::SplitBy;
|
||||
pub(crate) use split_column::SplitColumn;
|
||||
pub(crate) use split_row::SplitRow;
|
||||
pub(crate) use sum::Sum;
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use t_sort_by::TSortBy;
|
||||
pub(crate) use table::Table;
|
||||
|
55
crates/nu-cli/src/commands/sum.rs
Normal file
55
crates/nu-cli/src/commands/sum.rs
Normal file
@ -0,0 +1,55 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use crate::utils::data_processing::{reducer_for, Reduce};
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, ReturnValue, Signature, Value};
|
||||
use num_traits::identities::Zero;
|
||||
|
||||
pub struct Sum;
|
||||
|
||||
impl WholeStreamCommand for Sum {
|
||||
fn name(&self) -> &str {
|
||||
"sum"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("sum")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Sums the values."
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
sum(RunnableContext {
|
||||
input: args.input,
|
||||
commands: registry.clone(),
|
||||
shell_manager: args.shell_manager,
|
||||
host: args.host,
|
||||
source: args.call_info.source,
|
||||
ctrl_c: args.ctrl_c,
|
||||
name: args.call_info.name_tag,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn sum(RunnableContext { mut input, .. }: RunnableContext) -> Result<OutputStream, ShellError> {
|
||||
let stream = async_stream! {
|
||||
let mut values = input.drain_vec().await;
|
||||
|
||||
let action = reducer_for(Reduce::Sum);
|
||||
|
||||
match action(Value::zero(), values) {
|
||||
Ok(total) => yield ReturnSuccess::value(total),
|
||||
Err(err) => yield Err(err),
|
||||
}
|
||||
};
|
||||
|
||||
let stream: BoxStream<'static, ReturnValue> = stream.boxed();
|
||||
|
||||
Ok(stream.to_output_stream())
|
||||
}
|
@ -1,10 +1,11 @@
|
||||
use crate::data::value::compare_values;
|
||||
use crate::data::TaggedListBuilder;
|
||||
use chrono::{DateTime, NaiveDate, Utc};
|
||||
use nu_errors::ShellError;
|
||||
use nu_parser::CompareOperator;
|
||||
use nu_protocol::{Primitive, TaggedDictBuilder, UntaggedValue, Value};
|
||||
use nu_source::{SpannedItem, Tag, Tagged, TaggedItem};
|
||||
use nu_value_ext::{get_data_by_key, ValueExt};
|
||||
use num_bigint::BigInt;
|
||||
use num_traits::Zero;
|
||||
|
||||
pub fn columns_sorted(
|
||||
@ -196,44 +197,31 @@ pub fn evaluate(
|
||||
Ok(results)
|
||||
}
|
||||
|
||||
fn sum(data: Vec<Value>) -> Result<Value, ShellError> {
|
||||
let total = data
|
||||
pub fn sum(data: Vec<Value>) -> Result<Value, ShellError> {
|
||||
Ok(data
|
||||
.into_iter()
|
||||
.fold(Zero::zero(), |acc: BigInt, value| match value {
|
||||
Value {
|
||||
value: UntaggedValue::Primitive(Primitive::Int(n)),
|
||||
..
|
||||
} => acc + n,
|
||||
_ => acc,
|
||||
});
|
||||
|
||||
Ok(UntaggedValue::int(total).into_untagged_value())
|
||||
.fold(Value::zero(), |acc: Value, value| acc + value))
|
||||
}
|
||||
|
||||
fn formula(
|
||||
acc_begin: BigInt,
|
||||
calculator: Box<dyn Fn(Vec<Value>) -> Result<Value, ShellError> + 'static>,
|
||||
) -> Box<dyn Fn(BigInt, Vec<Value>) -> Result<Value, ShellError> + 'static> {
|
||||
acc_begin: Value,
|
||||
calculator: Box<dyn Fn(Vec<Value>) -> Result<Value, ShellError> + Send + Sync + 'static>,
|
||||
) -> Box<dyn Fn(Value, Vec<Value>) -> Result<Value, ShellError> + Send + Sync + 'static> {
|
||||
Box::new(move |acc, datax| -> Result<Value, ShellError> {
|
||||
let result = acc * acc_begin.clone();
|
||||
|
||||
if let Ok(Value {
|
||||
value: UntaggedValue::Primitive(Primitive::Int(computed)),
|
||||
..
|
||||
}) = calculator(datax)
|
||||
{
|
||||
return Ok(UntaggedValue::int(result + computed).into_untagged_value());
|
||||
match calculator(datax) {
|
||||
Ok(total) => Ok(result + total),
|
||||
Err(reason) => Err(reason),
|
||||
}
|
||||
|
||||
Ok(UntaggedValue::int(0).into_untagged_value())
|
||||
})
|
||||
}
|
||||
|
||||
pub fn reducer_for(
|
||||
command: Reduce,
|
||||
) -> Box<dyn Fn(BigInt, Vec<Value>) -> Result<Value, ShellError> + 'static> {
|
||||
) -> Box<dyn Fn(Value, Vec<Value>) -> Result<Value, ShellError> + Send + Sync + 'static> {
|
||||
match command {
|
||||
Reduce::Sum | Reduce::Default => Box::new(formula(Zero::zero(), Box::new(sum))),
|
||||
Reduce::Sum | Reduce::Default => Box::new(formula(Value::zero(), Box::new(sum))),
|
||||
}
|
||||
}
|
||||
|
||||
@ -262,7 +250,7 @@ pub fn reduce(
|
||||
let datasets: Vec<_> = datasets
|
||||
.iter()
|
||||
.map(|subsets| {
|
||||
let acc: BigInt = Zero::zero();
|
||||
let acc = Value::zero();
|
||||
match subsets {
|
||||
Value {
|
||||
value: UntaggedValue::Table(data),
|
||||
@ -318,37 +306,48 @@ pub fn map_max(
|
||||
value: UntaggedValue::Table(datasets),
|
||||
..
|
||||
} => {
|
||||
let datasets: Vec<_> = datasets
|
||||
let datasets: Vec<Value> = datasets
|
||||
.iter()
|
||||
.map(|subsets| match subsets {
|
||||
Value {
|
||||
value: UntaggedValue::Table(data),
|
||||
..
|
||||
} => {
|
||||
let data: BigInt =
|
||||
data.iter().fold(Zero::zero(), |acc, value| match value {
|
||||
Value {
|
||||
value: UntaggedValue::Primitive(Primitive::Int(n)),
|
||||
..
|
||||
} if *n > acc => n.clone(),
|
||||
_ => acc,
|
||||
});
|
||||
UntaggedValue::int(data).into_value(&tag)
|
||||
}
|
||||
} => data.iter().fold(Value::zero(), |acc, value| {
|
||||
let left = &value.value;
|
||||
let right = &acc.value;
|
||||
|
||||
if let Ok(is_greater_than) =
|
||||
compare_values(CompareOperator::GreaterThan, left, right)
|
||||
{
|
||||
if is_greater_than {
|
||||
value.clone()
|
||||
} else {
|
||||
acc
|
||||
}
|
||||
} else {
|
||||
acc
|
||||
}
|
||||
}),
|
||||
_ => UntaggedValue::int(0).into_value(&tag),
|
||||
})
|
||||
.collect();
|
||||
|
||||
let datasets: BigInt = datasets
|
||||
.iter()
|
||||
.fold(Zero::zero(), |max, value| match value {
|
||||
Value {
|
||||
value: UntaggedValue::Primitive(Primitive::Int(n)),
|
||||
..
|
||||
} if *n > max => n.clone(),
|
||||
_ => max,
|
||||
});
|
||||
UntaggedValue::int(datasets).into_value(&tag)
|
||||
datasets.into_iter().fold(Value::zero(), |max, value| {
|
||||
let left = &value.value;
|
||||
let right = &max.value;
|
||||
|
||||
if let Ok(is_greater_than) =
|
||||
compare_values(CompareOperator::GreaterThan, left, right)
|
||||
{
|
||||
if is_greater_than {
|
||||
value
|
||||
} else {
|
||||
max
|
||||
}
|
||||
} else {
|
||||
max
|
||||
}
|
||||
})
|
||||
}
|
||||
_ => UntaggedValue::int(-1).into_value(&tag),
|
||||
};
|
||||
@ -573,7 +572,7 @@ mod tests {
|
||||
|
||||
let action = reducer_for(Reduce::Sum);
|
||||
|
||||
assert_eq!(action(Zero::zero(), subject)?, int(3));
|
||||
assert_eq!(action(Value::zero(), subject)?, int(3));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ mod save;
|
||||
mod sort_by;
|
||||
mod split_by;
|
||||
mod split_column;
|
||||
mod sum;
|
||||
mod touch;
|
||||
mod uniq;
|
||||
mod where_;
|
||||
|
30
crates/nu-cli/tests/commands/sum.rs
Normal file
30
crates/nu-cli/tests/commands/sum.rs
Normal file
@ -0,0 +1,30 @@
|
||||
use nu_test_support::fs::Stub::FileWithContentToBeTrimmed;
|
||||
use nu_test_support::playground::Playground;
|
||||
use nu_test_support::{nu, pipeline};
|
||||
|
||||
#[test]
|
||||
fn all() {
|
||||
Playground::setup("sum_test_1", |dirs, sandbox| {
|
||||
sandbox.with_files(vec![FileWithContentToBeTrimmed(
|
||||
"meals.csv",
|
||||
r#"
|
||||
description,calories
|
||||
"1 large egg",90
|
||||
"1 cup white rice",250
|
||||
"1 tablespoon fish oil",108
|
||||
"#,
|
||||
)]);
|
||||
|
||||
let actual = nu!(
|
||||
cwd: dirs.test(), pipeline(
|
||||
r#"
|
||||
open meals.csv
|
||||
| get calories
|
||||
| sum
|
||||
| echo $it
|
||||
"#
|
||||
));
|
||||
|
||||
assert_eq!(actual, "448");
|
||||
})
|
||||
}
|
@ -4,7 +4,7 @@ use nu_test_support::{nu, pipeline};
|
||||
|
||||
#[test]
|
||||
fn wrap_rows_into_a_row() {
|
||||
Playground::setup("embed_test_1", |dirs, sandbox| {
|
||||
Playground::setup("wrap_test_1", |dirs, sandbox| {
|
||||
sandbox.with_files(vec![FileWithContentToBeTrimmed(
|
||||
"los_tres_caballeros.txt",
|
||||
r#"
|
||||
@ -34,7 +34,7 @@ fn wrap_rows_into_a_row() {
|
||||
|
||||
#[test]
|
||||
fn wrap_rows_into_a_table() {
|
||||
Playground::setup("embed_test_2", |dirs, sandbox| {
|
||||
Playground::setup("wrap_test_2", |dirs, sandbox| {
|
||||
sandbox.with_files(vec![FileWithContentToBeTrimmed(
|
||||
"los_tres_caballeros.txt",
|
||||
r#"
|
||||
|
Reference in New Issue
Block a user