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(MathAverage),
|
||||||
whole_stream_command(MathMedian),
|
whole_stream_command(MathMedian),
|
||||||
whole_stream_command(MathMinimum),
|
whole_stream_command(MathMinimum),
|
||||||
|
whole_stream_command(MathMode),
|
||||||
whole_stream_command(MathMaximum),
|
whole_stream_command(MathMaximum),
|
||||||
whole_stream_command(MathSummation),
|
whole_stream_command(MathSummation),
|
||||||
// File format output
|
// File format output
|
||||||
|
@ -198,7 +198,9 @@ 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, MathSummation};
|
pub(crate) use math::{
|
||||||
|
Math, MathAverage, MathMaximum, MathMedian, MathMinimum, MathMode, 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;
|
||||||
|
@ -35,11 +35,11 @@ impl WholeStreamCommand for Command {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::commands::math::{
|
use crate::commands::math::{
|
||||||
avg::average, max::maximum, median::median, min::minimum, sum::summation, utils::calculate,
|
avg::average, max::maximum, median::median, min::minimum, mode::mode, sum::summation,
|
||||||
utils::MathFunction,
|
utils::calculate, utils::MathFunction,
|
||||||
};
|
};
|
||||||
use nu_plugin::row;
|
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;
|
use nu_protocol::Value;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -74,6 +74,7 @@ mod tests {
|
|||||||
Ok(int(10)),
|
Ok(int(10)),
|
||||||
Ok(int(10)),
|
Ok(int(10)),
|
||||||
Ok(int(10)),
|
Ok(int(10)),
|
||||||
|
Ok(table(&vec![int(10)])),
|
||||||
Ok(int(10)),
|
Ok(int(10)),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -86,6 +87,7 @@ mod tests {
|
|||||||
Ok(int(10)),
|
Ok(int(10)),
|
||||||
Ok(int(30)),
|
Ok(int(30)),
|
||||||
Ok(int(20)),
|
Ok(int(20)),
|
||||||
|
Ok(table(&vec![int(10), int(20), int(30)])),
|
||||||
Ok(int(60)),
|
Ok(int(60)),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -98,6 +100,7 @@ mod tests {
|
|||||||
Ok(int(10)),
|
Ok(int(10)),
|
||||||
Ok(decimal(26.5)),
|
Ok(decimal(26.5)),
|
||||||
Ok(decimal(26.5)),
|
Ok(decimal(26.5)),
|
||||||
|
Ok(table(&vec![decimal(26.5)])),
|
||||||
Ok(decimal(63)),
|
Ok(decimal(63)),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -110,6 +113,7 @@ mod tests {
|
|||||||
Ok(int(-14)),
|
Ok(int(-14)),
|
||||||
Ok(int(10)),
|
Ok(int(10)),
|
||||||
Ok(int(-11)),
|
Ok(int(-11)),
|
||||||
|
Ok(table(&vec![int(-14), int(-11), int(10)])),
|
||||||
Ok(int(-15)),
|
Ok(int(-15)),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -122,6 +126,7 @@ mod tests {
|
|||||||
Ok(decimal(-13.5)),
|
Ok(decimal(-13.5)),
|
||||||
Ok(int(10)),
|
Ok(int(10)),
|
||||||
Ok(decimal(-11.5)),
|
Ok(decimal(-11.5)),
|
||||||
|
Ok(table(&vec![decimal(-13.5), decimal(-11.5), int(10)])),
|
||||||
Ok(decimal(-15)),
|
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(1), "col2".to_owned() => int(5)]),
|
||||||
Ok(row!["col1".to_owned() => int(4), "col2".to_owned() => int(8)]),
|
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() => 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)]),
|
Ok(row!["col1".to_owned() => int(10), "col2".to_owned() => int(26)]),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -154,8 +163,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> =
|
let math_functions: Vec<MathFunction> =
|
||||||
vec![average, minimum, maximum, median, summation];
|
vec![average, minimum, maximum, median, mode, summation];
|
||||||
|
|
||||||
let results = math_functions
|
let results = math_functions
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|mf| calculate(&tc.values, &test_tag, mf))
|
.map(|mf| calculate(&tc.values, &test_tag, mf))
|
||||||
|
@ -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 mode;
|
||||||
pub mod sum;
|
pub mod sum;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|
||||||
@ -11,4 +12,5 @@ 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 mode::SubCommand as MathMode;
|
||||||
pub use sum::SubCommand as MathSummation;
|
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 values: Vec<Value> = input.drain_vec().await;
|
||||||
let res = calculate(&values, &name, mf);
|
let res = calculate(&values, &name, mf);
|
||||||
match res {
|
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),
|
Err(e) => Err(e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user