diff --git a/crates/nu-command/src/filters/transpose.rs b/crates/nu-command/src/filters/transpose.rs index 9e8b871ebe..0a3761466b 100644 --- a/crates/nu-command/src/filters/transpose.rs +++ b/crates/nu-command/src/filters/transpose.rs @@ -15,6 +15,8 @@ pub struct TransposeArgs { header_row: bool, ignore_titles: bool, as_record: bool, + keep_last: bool, + keep_all: bool, } impl Command for Transpose { @@ -39,6 +41,16 @@ impl Command for Transpose { "transfer to record if the result is a table and contains only one row", Some('d'), ) + .switch( + "keep-last", + "on repetition of record fields due to `header-row`, keep the last value obtained", + Some('l'), + ) + .switch( + "keep-all", + "on repetition of record fields due to `header-row`, keep all the values obtained", + Some('a'), + ) .rest( "rest", SyntaxShape::String, @@ -149,6 +161,8 @@ pub fn transpose( header_row: call.has_flag("header-row"), ignore_titles: call.has_flag("ignore-titles"), as_record: call.has_flag("as-record"), + keep_last: call.has_flag("keep-last"), + keep_all: call.has_flag("keep-all"), rest: call.rest(engine_state, stack, 0)?, }; @@ -240,12 +254,82 @@ pub fn transpose( for i in input.clone() { match &i.get_data_by_key(&desc) { Some(x) => { - cols.push(headers[column_num].clone()); - vals.push(x.clone()); + if args.keep_all && cols.contains(&headers[column_num]) { + let index = cols + .iter() + .position(|y| y == &headers[column_num]) + .expect("value is contained."); + let new_val = match &vals[index] { + Value::List { vals, span } => { + let mut vals = vals.clone(); + vals.push(x.clone()); + Value::List { + vals: vals.to_vec(), + span: *span, + } + } + v => Value::List { + vals: vec![v.clone(), x.clone()], + span: v.span().expect("this should be a valid span"), + }, + }; + cols.remove(index); + vals.remove(index); + + cols.push(headers[column_num].clone()); + vals.push(new_val); + } else if args.keep_last && cols.contains(&headers[column_num]) { + let index = cols + .iter() + .position(|y| y == &headers[column_num]) + .expect("value is contained."); + cols.remove(index); + vals.remove(index); + cols.push(headers[column_num].clone()); + vals.push(x.clone()); + } else if !cols.contains(&headers[column_num]) { + cols.push(headers[column_num].clone()); + vals.push(x.clone()); + } } _ => { - cols.push(headers[column_num].clone()); - vals.push(Value::nothing(name)); + if args.keep_all && cols.contains(&headers[column_num]) { + let index = cols + .iter() + .position(|y| y == &headers[column_num]) + .expect("value is contained."); + let new_val = match &vals[index] { + Value::List { vals, span } => { + let mut vals = vals.clone(); + vals.push(Value::nothing(name)); + Value::List { + vals: vals.to_vec(), + span: *span, + } + } + v => Value::List { + vals: vec![v.clone(), Value::nothing(name)], + span: v.span().expect("this should be a valid span"), + }, + }; + cols.remove(index); + vals.remove(index); + + cols.push(headers[column_num].clone()); + vals.push(new_val); + } else if args.keep_last && cols.contains(&headers[column_num]) { + let index = cols + .iter() + .position(|y| y == &headers[column_num]) + .expect("value is contained."); + cols.remove(index); + vals.remove(index); + cols.push(headers[column_num].clone()); + vals.push(Value::nothing(name)); + } else if !cols.contains(&headers[column_num]) { + cols.push(headers[column_num].clone()); + vals.push(Value::nothing(name)); + } } } column_num += 1; diff --git a/crates/nu-command/tests/commands/mod.rs b/crates/nu-command/tests/commands/mod.rs index 662ff627cd..c534b87ba5 100644 --- a/crates/nu-command/tests/commands/mod.rs +++ b/crates/nu-command/tests/commands/mod.rs @@ -65,6 +65,7 @@ mod split_row; mod str_; mod take; mod touch; +mod transpose; mod uniq; mod update; mod upsert; diff --git a/crates/nu-command/tests/commands/transpose.rs b/crates/nu-command/tests/commands/transpose.rs new file mode 100644 index 0000000000..2b7fc56af0 --- /dev/null +++ b/crates/nu-command/tests/commands/transpose.rs @@ -0,0 +1,37 @@ +use nu_test_support::{nu, pipeline}; + +#[test] +fn row() { + let actual = nu!( + cwd: ".", pipeline( + r#" + [[key value]; [foo 1] [foo 2]] | transpose -r | debug + "# + )); + + assert!(actual.out.contains("foo: 1")); +} + +#[test] +fn row_but_last() { + let actual = nu!( + cwd: ".", pipeline( + r#" + [[key value]; [foo 1] [foo 2]] | transpose -r -l | debug + "# + )); + + assert!(actual.out.contains("foo: 2")); +} + +#[test] +fn row_but_all() { + let actual = nu!( + cwd: ".", pipeline( + r#" + [[key value]; [foo 1] [foo 2]] | transpose -r -a | debug + "# + )); + + assert!(actual.out.contains("foo: [1, 2]")); +}