diff --git a/crates/nu-command/src/filters/group_by.rs b/crates/nu-command/src/filters/group_by.rs index a2dc4d7631..790246dd20 100644 --- a/crates/nu-command/src/filters/group_by.rs +++ b/crates/nu-command/src/filters/group_by.rs @@ -22,10 +22,15 @@ impl Command for GroupBy { // example. Perhaps Table should be a subtype of List, in which case // the current signature would suffice even when a Table example // exists. - .input_output_types(vec![( - Type::List(Box::new(Type::Any)), - Type::Record(vec![]), - )]) + .input_output_types(vec![ + (Type::List(Box::new(Type::Any)), Type::Record(vec![])), + (Type::List(Box::new(Type::Any)), Type::Table(vec![])), + ]) + .switch( + "to-table", + "Return a table with \"groups\" and \"items\" columns", + None, + ) .optional( "grouper", SyntaxShape::OneOf(vec![ @@ -80,7 +85,6 @@ impl Command for GroupBy { ), })), }, - Example { description: "You can also group by raw values by leaving out the argument", example: "['1' '3' '1' '3' '2' '1' '1'] | group-by", @@ -101,6 +105,46 @@ impl Command for GroupBy { ), })), }, + Example { + description: "You can also output a table instead of a record", + example: "['1' '3' '1' '3' '2' '1' '1'] | group-by --to-table", + result: Some(Value::test_list(vec![ + Value::test_record( + record! { + "group" => Value::test_string("1"), + "items" => Value::test_list( + vec![ + Value::test_string("1"), + Value::test_string("1"), + Value::test_string("1"), + Value::test_string("1"), + ] + ) + } + ), + Value::test_record( + record! { + "group" => Value::test_string("3"), + "items" => Value::test_list( + vec![ + Value::test_string("3"), + Value::test_string("3"), + ] + ) + } + ), + Value::test_record( + record! { + "group" => Value::test_string("2"), + "items" => Value::test_list( + vec![ + Value::test_string("2"), + ] + ) + } + ), + ])), + }, ] } } @@ -123,11 +167,11 @@ pub fn group_by( )); } - let group_value = match grouper { + let groups = match grouper { Some(v) => { let span = v.span(); match v { - Value::CellPath { val, .. } => group_cell_path(val, values, span)?, + Value::CellPath { val, .. } => group_cell_path(val, values)?, Value::Block { .. } | Value::Closure { .. } => { let block: Option = call.opt(engine_state, stack, 0)?; group_closure(&values, span, block, stack, engine_state, call)? @@ -141,17 +185,22 @@ pub fn group_by( } } } - None => group_no_grouper(values, span)?, + None => group_no_grouper(values)?, }; - Ok(PipelineData::Value(group_value, None)) + let value = if call.has_flag("to-table") { + groups_to_table(groups, span) + } else { + groups_to_record(groups, span) + }; + + Ok(PipelineData::Value(value, None)) } pub fn group_cell_path( column_name: CellPath, values: Vec, - span: Span, -) -> Result { +) -> Result>, ShellError> { let mut groups: IndexMap> = IndexMap::new(); for value in values.into_iter() { @@ -167,16 +216,10 @@ pub fn group_cell_path( group.push(value); } - Ok(Value::record( - groups - .into_iter() - .map(|(k, v)| (k, Value::list(v, span))) - .collect(), - span, - )) + Ok(groups) } -pub fn group_no_grouper(values: Vec, span: Span) -> Result { +pub fn group_no_grouper(values: Vec) -> Result>, ShellError> { let mut groups: IndexMap> = IndexMap::new(); for value in values.into_iter() { @@ -185,13 +228,7 @@ pub fn group_no_grouper(values: Vec, span: Span) -> Result Result { +) -> Result>, ShellError> { let error_key = "error"; let mut keys: Vec> = vec![]; let value_list = Value::list(values.to_vec(), span); @@ -268,13 +305,35 @@ fn group_closure( group.push(value); } - Ok(Value::record( + Ok(groups) +} + +fn groups_to_record(groups: IndexMap>, span: Span) -> Value { + Value::record( groups .into_iter() .map(|(k, v)| (k, Value::list(v, span))) .collect(), span, - )) + ) +} + +fn groups_to_table(groups: IndexMap>, span: Span) -> Value { + Value::list( + groups + .into_iter() + .map(|(group, items)| { + Value::record( + record! { + "group" => Value::string(group, span), + "items" => Value::list(items, span), + }, + span, + ) + }) + .collect(), + span, + ) } #[cfg(test)] diff --git a/crates/nu-std/std/testing.nu b/crates/nu-std/std/testing.nu index 5049dd7065..4880bf93e5 100644 --- a/crates/nu-std/std/testing.nu +++ b/crates/nu-std/std/testing.nu @@ -72,11 +72,10 @@ def create-test-record [] nothing -> record