From 0e86430ea34fba749c8d7da02f89f1659cc5e264 Mon Sep 17 00:00:00 2001 From: "notryanb@gmail.com" Date: Tue, 8 Oct 2019 22:17:02 -0400 Subject: [PATCH 1/7] get very basic average working --- Cargo.toml | 4 ++ src/plugins/average.rs | 106 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 src/plugins/average.rs diff --git a/Cargo.toml b/Cargo.toml index cd6be5d9fa..16b8c85863 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -119,6 +119,10 @@ path = "src/plugins/inc.rs" name = "nu_plugin_sum" path = "src/plugins/sum.rs" +[[bin]] +name = "nu_plugin_average" +path = "src/plugins/average.rs" + [[bin]] name = "nu_plugin_embed" path = "src/plugins/embed.rs" diff --git a/src/plugins/average.rs b/src/plugins/average.rs new file mode 100644 index 0000000000..5e76560d40 --- /dev/null +++ b/src/plugins/average.rs @@ -0,0 +1,106 @@ +use nu::{ + serve_plugin, CoerceInto, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature, + Tagged, TaggedItem, Value, +}; + +#[derive(Debug)] +struct Average { + total: Option>, + count: u64, +} + +impl Average { + fn new() -> Average { + Average { total: None, count: 1 } + } + + fn average(&mut self, value: Tagged) -> Result<(), ShellError> { + match value.item() { + Value::Primitive(Primitive::Nothing) => Ok(()), + Value::Primitive(Primitive::Int(i)) => { + match &self.total { + Some(Tagged { + item: Value::Primitive(Primitive::Int(j)), + tag, + }) => { + self.total = Some(Value::int(i + j).tagged(tag)); + self.count = self.count + 1; + Ok(()) + } + None => { + self.total = Some(value.clone()); + Ok(()) + } + _ => Err(ShellError::string(format!( + "Could not calculate average of non-integer or unrelated types" + ))), + } + } + Value::Primitive(Primitive::Bytes(b)) => { + match self.total { + Some(Tagged { + item: Value::Primitive(Primitive::Bytes(j)), + tag, + }) => { + self.total = Some(Value::int(b + j).tagged(tag)); + self.count = self.count + 1; + Ok(()) + } + None => { + self.total = Some(value); + Ok(()) + } + _ => Err(ShellError::string(format!( + "Could not calculate average of non-integer or unrelated types" + ))), + } + } + x => Err(ShellError::string(format!( + "Unrecognized type in stream: {:?}", + x + ))), + } + + } +} + +impl Plugin for Average { + fn config(&mut self) -> Result { + Ok(Signature::build("average") + .desc("Compute the average of a column of numerical values.") + .filter()) + } + + fn begin_filter(&mut self, _: CallInfo) -> Result, ShellError> { + Ok(vec![]) + } + + fn filter(&mut self, input: Tagged) -> Result, ShellError> { + self.average(input)?; + Ok(vec![]) + } + + fn end_filter(&mut self) -> Result, ShellError> { + match self.total { + None => Ok(vec![]), + Some(ref v) => { + match v.item() { + Value::Primitive(Primitive::Int(i)) => { + let total: u64 = i.tagged(v.tag).coerce_into("converting for average")?; + let avg = total as f64 / self.count as f64; + let decimal_value: Value= Primitive::from(avg).into(); + let tagged_value = decimal_value.tagged(v.tag); + Ok(vec![ReturnSuccess::value(tagged_value)]) + } + _ => unreachable!() + + } + }, + } + } +} + +fn main() { + serve_plugin(&mut Average::new()); +} + From 8262c2dd333137886480fc331bf3cbe800bf1afd Mon Sep 17 00:00:00 2001 From: "notryanb@gmail.com" Date: Sun, 13 Oct 2019 21:08:14 -0400 Subject: [PATCH 2/7] add support for average on byte columns and fmt the code --- src/plugins/average.rs | 118 +++++++++++++++++++++++------------------ 1 file changed, 65 insertions(+), 53 deletions(-) diff --git a/src/plugins/average.rs b/src/plugins/average.rs index 5e76560d40..8f82a23d10 100644 --- a/src/plugins/average.rs +++ b/src/plugins/average.rs @@ -1,6 +1,6 @@ use nu::{ - serve_plugin, CoerceInto, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature, - Tagged, TaggedItem, Value, + serve_plugin, CallInfo, CoerceInto, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, + Signature, Tagged, TaggedItem, Value, }; #[derive(Debug)] @@ -11,56 +11,61 @@ struct Average { impl Average { fn new() -> Average { - Average { total: None, count: 1 } + Average { + total: None, + count: 0, + } } fn average(&mut self, value: Tagged) -> Result<(), ShellError> { match value.item() { Value::Primitive(Primitive::Nothing) => Ok(()), - Value::Primitive(Primitive::Int(i)) => { - match &self.total { - Some(Tagged { - item: Value::Primitive(Primitive::Int(j)), - tag, - }) => { - self.total = Some(Value::int(i + j).tagged(tag)); - self.count = self.count + 1; - Ok(()) - } - None => { - self.total = Some(value.clone()); - Ok(()) - } - _ => Err(ShellError::string(format!( - "Could not calculate average of non-integer or unrelated types" - ))), + Value::Primitive(Primitive::Int(i)) => match &self.total { + Some(Tagged { + item: Value::Primitive(Primitive::Int(j)), + tag, + }) => { + self.total = Some(Value::int(i + j).tagged(tag)); + self.count += 1; + Ok(()) } - } - Value::Primitive(Primitive::Bytes(b)) => { - match self.total { - Some(Tagged { - item: Value::Primitive(Primitive::Bytes(j)), - tag, - }) => { - self.total = Some(Value::int(b + j).tagged(tag)); - self.count = self.count + 1; - Ok(()) - } - None => { - self.total = Some(value); - Ok(()) - } - _ => Err(ShellError::string(format!( - "Could not calculate average of non-integer or unrelated types" - ))), + None => { + self.total = Some(value.clone()); + self.count += 1; + Ok(()) } - } - x => Err(ShellError::string(format!( - "Unrecognized type in stream: {:?}", - x - ))), + _ => Err(ShellError::labeled_error( + "Could calculate average of non-integer or unrelated types", + "source", + value.tag, + )), + }, + Value::Primitive(Primitive::Bytes(b)) => match &self.total { + Some(Tagged { + item: Value::Primitive(Primitive::Bytes(j)), + tag, + }) => { + self.total = Some(Value::bytes(b + j).tagged(tag)); + self.count += 1; + Ok(()) + } + None => { + self.total = Some(value); + self.count += 1; + Ok(()) + } + _ => Err(ShellError::labeled_error( + "Could calculate average of non-integer or unrelated types", + "source", + value.tag, + )), + }, + x => Err(ShellError::labeled_error( + format!("Unrecognized type in stream: {:?}", x), + "source", + value.tag, + )), } - } } @@ -83,19 +88,27 @@ impl Plugin for Average { fn end_filter(&mut self) -> Result, ShellError> { match self.total { None => Ok(vec![]), - Some(ref v) => { - match v.item() { + Some(ref inner) => { + match inner.item() { Value::Primitive(Primitive::Int(i)) => { - let total: u64 = i.tagged(v.tag).coerce_into("converting for average")?; + let total: u64 = i + .tagged(inner.tag.clone()) + .coerce_into("converting for average")?; let avg = total as f64 / self.count as f64; - let decimal_value: Value= Primitive::from(avg).into(); - let tagged_value = decimal_value.tagged(v.tag); + let primitive_value: Value = Primitive::from(avg).into(); + let tagged_value = primitive_value.tagged(inner.tag.clone()); Ok(vec![ReturnSuccess::value(tagged_value)]) } - _ => unreachable!() - + Value::Primitive(Primitive::Bytes(bytes)) => { + // let total: u64 = b.tagged(inner.tag.clone()).coerce_into("converting for average")?; + let avg = *bytes as f64 / self.count as f64; + let primitive_value: Value = Primitive::from(avg).into(); + let tagged_value = primitive_value.tagged(inner.tag.clone()); + Ok(vec![ReturnSuccess::value(tagged_value)]) + } + _ => Ok(vec![]), } - }, + } } } } @@ -103,4 +116,3 @@ impl Plugin for Average { fn main() { serve_plugin(&mut Average::new()); } - From 43fbf4345d626bc1d0d6fbeb0c03c8ffc9c538fa Mon Sep 17 00:00:00 2001 From: "notryanb@gmail.com" Date: Mon, 14 Oct 2019 17:55:42 -0400 Subject: [PATCH 3/7] remove comment and add test for averaging integers --- src/plugins/average.rs | 37 +++++++++++++++++-------------------- tests/filters_test.rs | 15 +++++++++++++++ 2 files changed, 32 insertions(+), 20 deletions(-) diff --git a/src/plugins/average.rs b/src/plugins/average.rs index 8f82a23d10..f78078450a 100644 --- a/src/plugins/average.rs +++ b/src/plugins/average.rs @@ -88,27 +88,24 @@ impl Plugin for Average { fn end_filter(&mut self) -> Result, ShellError> { match self.total { None => Ok(vec![]), - Some(ref inner) => { - match inner.item() { - Value::Primitive(Primitive::Int(i)) => { - let total: u64 = i - .tagged(inner.tag.clone()) - .coerce_into("converting for average")?; - let avg = total as f64 / self.count as f64; - let primitive_value: Value = Primitive::from(avg).into(); - let tagged_value = primitive_value.tagged(inner.tag.clone()); - Ok(vec![ReturnSuccess::value(tagged_value)]) - } - Value::Primitive(Primitive::Bytes(bytes)) => { - // let total: u64 = b.tagged(inner.tag.clone()).coerce_into("converting for average")?; - let avg = *bytes as f64 / self.count as f64; - let primitive_value: Value = Primitive::from(avg).into(); - let tagged_value = primitive_value.tagged(inner.tag.clone()); - Ok(vec![ReturnSuccess::value(tagged_value)]) - } - _ => Ok(vec![]), + Some(ref inner) => match inner.item() { + Value::Primitive(Primitive::Int(i)) => { + let total: u64 = i + .tagged(inner.tag.clone()) + .coerce_into("converting for average")?; + let avg = total as f64 / self.count as f64; + let primitive_value: Value = Primitive::from(avg).into(); + let tagged_value = primitive_value.tagged(inner.tag.clone()); + Ok(vec![ReturnSuccess::value(tagged_value)]) } - } + Value::Primitive(Primitive::Bytes(bytes)) => { + let avg = *bytes as f64 / self.count as f64; + let primitive_value: Value = Primitive::from(avg).into(); + let tagged_value = primitive_value.tagged(inner.tag.clone()); + Ok(vec![ReturnSuccess::value(tagged_value)]) + } + _ => Ok(vec![]), + }, } } } diff --git a/tests/filters_test.rs b/tests/filters_test.rs index f0d5dead61..7696b2f80b 100644 --- a/tests/filters_test.rs +++ b/tests/filters_test.rs @@ -579,6 +579,21 @@ fn can_sum() { assert_eq!(actual, "203") } +#[test] +fn can_average() { + let actual = nu!( + cwd: "tests/fixtures/formats", h::pipeline( + r#" + open sgml_description.json + | get glossary.GlossDiv.GlossList.GlossEntry.Sections + | average + | echo $it + "# + )); + + assert_eq!(actual, "101.5000000000000") +} + #[test] fn can_filter_by_unit_size_comparison() { let actual = nu!( From f9fbb0eb3c3a4482afd5eaaebb90c6a743fa6aaf Mon Sep 17 00:00:00 2001 From: "notryanb@gmail.com" Date: Wed, 16 Oct 2019 20:40:19 -0400 Subject: [PATCH 4/7] add docs for average and give more specific examples for sum --- docs/commands/average.md | 42 ++++++++++++++++++++++++++++++++++++++++ docs/commands/sum.md | 42 ++++++++++++++++++++++++---------------- 2 files changed, 67 insertions(+), 17 deletions(-) create mode 100644 docs/commands/average.md diff --git a/docs/commands/average.md b/docs/commands/average.md new file mode 100644 index 0000000000..701ad6091b --- /dev/null +++ b/docs/commands/average.md @@ -0,0 +1,42 @@ +# average This command allows you to calculate the average of values in a column. ## Examples +To get the average of the file sizes in a directory, simply pipe the size column from the ls command to the sum command. + +```shell +> ls | get size | average +━━━━━━━━━ + +━━━━━━━━━ +2282.727272727273 +━━━━━━━━━ +``` + +```shell +> pwd | split-row / | size | get chars | average +━━━━━━━━━ + +━━━━━━━━━ +5.250000000000000 +━━━━━━━━━ +``` + +Note that average only works for integer and byte values at the moment, and if the shell doesn't recognize the values in a column as one of those types, it will return an error. +One way to solve this is to convert each row to an integer and then pipe the result to `average` + +```shell +> open tests/fixtures/formats/caco3_plastics.csv | get tariff_item | average +error: Unrecognized type in stream: Primitive(String("2509000000")) +- shell:1:0 +1 | open tests/fixtures/formats/caco3_plastics.csv | get tariff_item | average + | ^^^^ source +``` + +```shell +> open tests/fixtures/formats/caco3_plastics.csv | get tariff_item | str --to-int | average +━━━━━━━━━━━━━━━━━━━ + +─────────────────── + 3239404444.000000 +━━━━━━━━━━━━━━━━━━━ +``` + + diff --git a/docs/commands/sum.md b/docs/commands/sum.md index f5c59848dd..f20dcb5f37 100644 --- a/docs/commands/sum.md +++ b/docs/commands/sum.md @@ -1,9 +1,4 @@ -# sum - -This command allows you to calculate the sum of values in a column. - -## Examples - +# sum This command allows you to calculate the sum of values in a column. ## Examples 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 @@ -15,21 +10,34 @@ To get the sum of the file sizes in a directory, simply pipe the size column fro ━━━━━━━━━ ``` +To get the sum of the characters in your present working directory. +```shell +> pwd | split-row / | size | get chars | sum +━━━━━━━━━ + +━━━━━━━━━ +21 +━━━━━━━━━ +``` + + + Note that sum only works for integer and byte values at the moment, and if the shell doesn't recognize the values in a column as one of those types, it will return an error. +One way to solve this is to convert each row to an integer and then pipe the result to `sum` ```shell -> open example.csv -━━━┯━━━━━━━━━┯━━━━━━━━┯━━━━━━━━━━ - # │ fruit │ amount │ quality -───┼─────────┼────────┼────────── - 0 │ apples │ 1 │ fresh - 1 │ bananas │ 2 │ old - 2 │ oranges │ 7 │ fresh - 3 │ kiwis │ 25 │ rotten -━━━┷━━━━━━━━━┷━━━━━━━━┷━━━━━━━━━━ +> open tests/fixtures/formats/caco3_plastics.csv | get tariff_item | average +error: Unrecognized type in stream: Primitive(String("2509000000")) +- shell:1:0 +1 | open tests/fixtures/formats/caco3_plastics.csv | get tariff_item | sum + | ^^^^ source ``` ```shell -> open example.csv | get amount | sum -error: Unrecognized type in stream: Primitive(String("1")) +> open tests/fixtures/formats/caco3_plastics.csv | get tariff_item | str --to-int | sum +━━━━━━━━━━━━━ + +───────────── + 29154639996 +━━━━━━━━━━━━━ ``` From 2f5eeab56745ba96662f05aade730e45bf74d796 Mon Sep 17 00:00:00 2001 From: "notryanb@gmail.com" Date: Wed, 16 Oct 2019 20:45:23 -0400 Subject: [PATCH 5/7] fix typos and incorrect commands --- docs/commands/average.md | 11 +++++++---- docs/commands/sum.md | 15 ++++++++------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/docs/commands/average.md b/docs/commands/average.md index 701ad6091b..d4095e518f 100644 --- a/docs/commands/average.md +++ b/docs/commands/average.md @@ -1,5 +1,8 @@ -# average This command allows you to calculate the average of values in a column. ## Examples -To get the average of the file sizes in a directory, simply pipe the size column from the ls command to the sum command. +# average +This command allows you to calculate the average of values in a column. + +## 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. ```shell > ls | get size | average @@ -19,8 +22,8 @@ To get the average of the file sizes in a directory, simply pipe the size column ━━━━━━━━━ ``` -Note that average only works for integer and byte values at the moment, and if the shell doesn't recognize the values in a column as one of those types, it will return an error. -One way to solve this is to convert each row to an integer and then pipe the result to `average` +Note that average only works for integer and byte values. If the shell doesn't recognize the values in a column as one of those types, it will return an error. +One way to solve this is to convert each row to an integer when possible and then pipe the result to `average` ```shell > open tests/fixtures/formats/caco3_plastics.csv | get tariff_item | average diff --git a/docs/commands/sum.md b/docs/commands/sum.md index f20dcb5f37..7482ca0c54 100644 --- a/docs/commands/sum.md +++ b/docs/commands/sum.md @@ -1,4 +1,7 @@ -# sum This command allows you to calculate the sum of values in a column. ## Examples +# sum +This command allows you to calculate the sum of values in a column. + +## Examples 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 @@ -10,7 +13,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 in your present working directory. +To get the sum of the characters that make up your present working directory. ```shell > pwd | split-row / | size | get chars | sum ━━━━━━━━━ @@ -20,13 +23,11 @@ To get the sum of the characters in your present working directory. ━━━━━━━━━ ``` - - -Note that sum only works for integer and byte values at the moment, and if the shell doesn't recognize the values in a column as one of those types, it will return an error. -One way to solve this is to convert each row to an integer and then pipe the result to `sum` +Note that sum only works for integer and byte values. If the shell doesn't recognize the values in a column as one of those types, it will return an error. +One way to solve this is to convert each row to an integer when possible and then pipe the result to `sum` ```shell -> open tests/fixtures/formats/caco3_plastics.csv | get tariff_item | average +> open tests/fixtures/formats/caco3_plastics.csv | get tariff_item | sum error: Unrecognized type in stream: Primitive(String("2509000000")) - shell:1:0 1 | open tests/fixtures/formats/caco3_plastics.csv | get tariff_item | sum From 4f91d2512a8952be06738d9797a084dc32c75734 Mon Sep 17 00:00:00 2001 From: "notryanb@gmail.com" Date: Wed, 16 Oct 2019 20:45:37 -0400 Subject: [PATCH 6/7] add a test to calculate average of bytes --- tests/filters_test.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/tests/filters_test.rs b/tests/filters_test.rs index 7696b2f80b..0754f76d20 100644 --- a/tests/filters_test.rs +++ b/tests/filters_test.rs @@ -580,7 +580,7 @@ fn can_sum() { } #[test] -fn can_average() { +fn can_average_numbers() { let actual = nu!( cwd: "tests/fixtures/formats", h::pipeline( r#" @@ -594,6 +594,16 @@ fn can_average() { assert_eq!(actual, "101.5000000000000") } +#[test] +fn can_average_bytes() { + let actual = nu!( + cwd: "tests/fixtures/formats", + "ls | get size | average | echo $it" + ); + + assert_eq!(actual, "2282.727272727273"); +} + #[test] fn can_filter_by_unit_size_comparison() { let actual = nu!( From 9eda573a434bc4d9fe41d04815c9662256eba1d0 Mon Sep 17 00:00:00 2001 From: "notryanb@gmail.com" Date: Fri, 18 Oct 2019 20:43:07 -0400 Subject: [PATCH 7/7] filter out the files that have the same size on multiple operating systems --- tests/filters_test.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/filters_test.rs b/tests/filters_test.rs index 0754f76d20..1eb55448b7 100644 --- a/tests/filters_test.rs +++ b/tests/filters_test.rs @@ -598,10 +598,10 @@ fn can_average_numbers() { fn can_average_bytes() { let actual = nu!( cwd: "tests/fixtures/formats", - "ls | get size | average | echo $it" + "ls | sort-by name | skip 1 | first 2 | get size | average | echo $it" ); - assert_eq!(actual, "2282.727272727273"); + assert_eq!(actual, "1600.000000000000"); } #[test]