mirror of
https://github.com/nushell/nushell.git
synced 2024-11-22 08:23:24 +01:00
Make mode subcommand: math mode (#2043)
* Update calculate to return a table when Value is a table * impl mode subcommand for math * add tests for math mode subcommand * add table/row tests for math mode subcommand * fix formatting
This commit is contained in:
parent
72f7406057
commit
93144a0132
693
Cargo.lock
generated
693
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -351,6 +351,7 @@ pub fn create_default_context(
|
||||
whole_stream_command(MathAverage),
|
||||
whole_stream_command(MathMedian),
|
||||
whole_stream_command(MathMinimum),
|
||||
whole_stream_command(MathMode),
|
||||
whole_stream_command(MathMaximum),
|
||||
whole_stream_command(MathSummation),
|
||||
// File format output
|
||||
|
@ -198,7 +198,9 @@ 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, MathSummation};
|
||||
pub(crate) use math::{
|
||||
Math, MathAverage, MathMaximum, MathMedian, MathMinimum, MathMode, MathSummation,
|
||||
};
|
||||
pub(crate) use merge::Merge;
|
||||
pub(crate) use mkdir::Mkdir;
|
||||
pub(crate) use mv::Move;
|
||||
|
@ -35,11 +35,11 @@ impl WholeStreamCommand for Command {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::commands::math::{
|
||||
avg::average, max::maximum, median::median, min::minimum, sum::summation, utils::calculate,
|
||||
utils::MathFunction,
|
||||
avg::average, max::maximum, median::median, min::minimum, mode::mode, sum::summation,
|
||||
utils::calculate, utils::MathFunction,
|
||||
};
|
||||
use nu_plugin::row;
|
||||
use nu_plugin::test_helpers::value::{decimal, int};
|
||||
use nu_plugin::test_helpers::value::{decimal, int, table};
|
||||
use nu_protocol::Value;
|
||||
|
||||
#[test]
|
||||
@ -74,6 +74,7 @@ mod tests {
|
||||
Ok(int(10)),
|
||||
Ok(int(10)),
|
||||
Ok(int(10)),
|
||||
Ok(table(&vec![int(10)])),
|
||||
Ok(int(10)),
|
||||
],
|
||||
},
|
||||
@ -86,6 +87,7 @@ mod tests {
|
||||
Ok(int(10)),
|
||||
Ok(int(30)),
|
||||
Ok(int(20)),
|
||||
Ok(table(&vec![int(10), int(20), int(30)])),
|
||||
Ok(int(60)),
|
||||
],
|
||||
},
|
||||
@ -98,6 +100,7 @@ mod tests {
|
||||
Ok(int(10)),
|
||||
Ok(decimal(26.5)),
|
||||
Ok(decimal(26.5)),
|
||||
Ok(table(&vec![decimal(26.5)])),
|
||||
Ok(decimal(63)),
|
||||
],
|
||||
},
|
||||
@ -110,6 +113,7 @@ mod tests {
|
||||
Ok(int(-14)),
|
||||
Ok(int(10)),
|
||||
Ok(int(-11)),
|
||||
Ok(table(&vec![int(-14), int(-11), int(10)])),
|
||||
Ok(int(-15)),
|
||||
],
|
||||
},
|
||||
@ -122,6 +126,7 @@ mod tests {
|
||||
Ok(decimal(-13.5)),
|
||||
Ok(int(10)),
|
||||
Ok(decimal(-11.5)),
|
||||
Ok(table(&vec![decimal(-13.5), decimal(-11.5), int(10)])),
|
||||
Ok(decimal(-15)),
|
||||
],
|
||||
},
|
||||
@ -139,6 +144,10 @@ mod tests {
|
||||
Ok(row!["col1".to_owned() => int(1), "col2".to_owned() => int(5)]),
|
||||
Ok(row!["col1".to_owned() => int(4), "col2".to_owned() => int(8)]),
|
||||
Ok(row!["col1".to_owned() => decimal(2.5), "col2".to_owned() => decimal(6.5)]),
|
||||
Ok(row![
|
||||
"col1".to_owned() => table(&vec![int(1), int(2), int(3), int(4)]),
|
||||
"col2".to_owned() => table(&vec![int(5), int(6), int(7), int(8)])
|
||||
]),
|
||||
Ok(row!["col1".to_owned() => int(10), "col2".to_owned() => int(26)]),
|
||||
],
|
||||
},
|
||||
@ -154,8 +163,7 @@ mod tests {
|
||||
for tc in tt.iter() {
|
||||
let tc: &TestCase = tc; // Just for type annotations
|
||||
let math_functions: Vec<MathFunction> =
|
||||
vec![average, minimum, maximum, median, summation];
|
||||
|
||||
vec![average, minimum, maximum, median, mode, summation];
|
||||
let results = math_functions
|
||||
.into_iter()
|
||||
.map(|mf| calculate(&tc.values, &test_tag, mf))
|
||||
|
@ -3,6 +3,7 @@ pub mod command;
|
||||
pub mod max;
|
||||
pub mod median;
|
||||
pub mod min;
|
||||
pub mod mode;
|
||||
pub mod sum;
|
||||
pub mod utils;
|
||||
|
||||
@ -11,4 +12,5 @@ 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 mode::SubCommand as MathMode;
|
||||
pub use sum::SubCommand as MathSummation;
|
||||
|
94
crates/nu-cli/src/commands/math/mode.rs
Normal file
94
crates/nu-cli/src/commands/math/mode.rs
Normal file
@ -0,0 +1,94 @@
|
||||
use crate::commands::math::utils::run_with_function;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Signature, UntaggedValue, Value};
|
||||
use std::cmp::Ordering;
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"math mode"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("math mode")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Gets the most frequent element(s) from a list of numbers or tables"
|
||||
}
|
||||
|
||||
async fn run(
|
||||
&self,
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
run_with_function(
|
||||
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,
|
||||
},
|
||||
mode,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Get the mode(s) of a list of numbers",
|
||||
example: "echo [3 3 9 12 12 15] | math mode",
|
||||
result: Some(vec![
|
||||
UntaggedValue::int(3).into_untagged_value(),
|
||||
UntaggedValue::int(12).into_untagged_value(),
|
||||
]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mode(values: &[Value], name: &Tag) -> Result<Value, ShellError> {
|
||||
let mut frequency_map = std::collections::HashMap::new();
|
||||
for v in values {
|
||||
let counter = frequency_map.entry(v.value.clone()).or_insert(0);
|
||||
*counter += 1;
|
||||
}
|
||||
|
||||
let mut max_freq = -1;
|
||||
let mut modes = Vec::<Value>::new();
|
||||
for (value, frequency) in frequency_map.iter() {
|
||||
match max_freq.cmp(&frequency) {
|
||||
Ordering::Less => {
|
||||
max_freq = *frequency;
|
||||
modes.clear();
|
||||
modes.push(value.clone().into_value(name));
|
||||
}
|
||||
Ordering::Equal => {
|
||||
modes.push(value.clone().into_value(name));
|
||||
}
|
||||
Ordering::Greater => (),
|
||||
}
|
||||
}
|
||||
|
||||
crate::commands::sort_by::sort(&mut modes, &[], name)?;
|
||||
Ok(UntaggedValue::Table(modes).into_value(name))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::SubCommand;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(SubCommand {})
|
||||
}
|
||||
}
|
@ -15,7 +15,17 @@ pub async fn run_with_function(
|
||||
let values: Vec<Value> = input.drain_vec().await;
|
||||
let res = calculate(&values, &name, mf);
|
||||
match res {
|
||||
Ok(v) => Ok(OutputStream::one(ReturnSuccess::value(v))),
|
||||
Ok(v) => {
|
||||
if v.value.is_table() {
|
||||
Ok(OutputStream::from(
|
||||
v.table_entries()
|
||||
.map(|v| ReturnSuccess::value(v.clone()))
|
||||
.collect::<Vec<_>>(),
|
||||
))
|
||||
} else {
|
||||
Ok(OutputStream::one(ReturnSuccess::value(v)))
|
||||
}
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user