diff --git a/crates/nu-command/src/filters/reject.rs b/crates/nu-command/src/filters/reject.rs index e6a6adffa..fdb63fcfe 100644 --- a/crates/nu-command/src/filters/reject.rs +++ b/crates/nu-command/src/filters/reject.rs @@ -1,10 +1,12 @@ use nu_engine::CallExt; -use nu_protocol::ast::{Call, CellPath}; +use nu_protocol::ast::{Call, CellPath, PathMember}; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ Category, Example, IntoPipelineData, PipelineData, Record, ShellError, Signature, Span, SyntaxShape, Type, Value, }; +use std::cmp::Reverse; +use std::collections::HashSet; #[derive(Clone)] pub struct Reject; @@ -70,6 +72,17 @@ impl Command for Reject { Span::test_data(), )), }, + Example { + description: "Reject a row in a table", + example: "[[a, b]; [1, 2] [3, 4]] | reject 1", + result: Some(Value::List { + vals: vec![Value::test_record(Record { + cols: vec!["a".to_string(), "b".to_string()], + vals: vec![Value::test_int(1), Value::test_int(2)], + })], + span: Span::test_data(), + }), + }, Example { description: "Reject the specified field in a record", example: "{a: 1, b: 2} | reject a", @@ -99,15 +112,47 @@ fn reject( input: PipelineData, cell_paths: Vec, ) -> Result { + let mut unique_rows: HashSet = HashSet::new(); let val = input.into_value(span); let mut val = val; - let mut columns = vec![]; - for c in cell_paths { - if !columns.contains(&c) { - columns.push(c); - } + let mut new_columns = vec![]; + let mut new_rows = vec![]; + for column in cell_paths { + let CellPath { ref members } = column; + match members.get(0) { + Some(PathMember::Int { val, span, .. }) => { + if members.len() > 1 { + return Err(ShellError::GenericError( + "Reject only allows row numbers for rows".into(), + "extra after row number".into(), + Some(*span), + None, + Vec::new(), + )); + } + if !unique_rows.contains(val) { + unique_rows.insert(*val); + new_rows.push(column); + } + } + _ => { + if !new_columns.contains(&column) { + new_columns.push(column) + } + } + }; } - for cell_path in columns { + new_rows.sort_unstable_by_key(|k| { + Reverse({ + match k.members[0] { + PathMember::Int { val, .. } => val, + PathMember::String { .. } => usize::MIN, + } + }) + }); + + new_columns.append(&mut new_rows); + for cell_path in new_columns { val.remove_data_at_cell_path(&cell_path.members)?; } Ok(val.into_pipeline_data()) diff --git a/crates/nu-command/tests/commands/reject.rs b/crates/nu-command/tests/commands/reject.rs index 8ab25784f..d073a9be3 100644 --- a/crates/nu-command/tests/commands/reject.rs +++ b/crates/nu-command/tests/commands/reject.rs @@ -79,6 +79,12 @@ fn ignores_duplicate_columns_rejected() { assert_eq!(actual.out, "last name"); } +#[test] +fn ignores_duplicate_rows_rejected() { + let actual = nu!("[[a,b];[1 2] [3 4] [5 6]] | reject 2 2 | to nuon"); + assert_eq!(actual.out, "[[a, b]; [1, 2], [3, 4]]"); +} + #[test] fn reject_record_from_raw_eval() { let actual = nu!(r#"{"a": 3} | reject a | describe"#); @@ -143,3 +149,15 @@ fn reject_optional_row() { let actual = nu!("[{foo: 'bar'}] | reject 3? | to nuon"); assert_eq!(actual.out, "[[foo]; [bar]]"); } + +#[test] +fn reject_multiple_rows_ascending() { + let actual = nu!("[[a,b];[1 2] [3 4] [5 6]] | reject 1 2 | to nuon"); + assert_eq!(actual.out, "[[a, b]; [1, 2]]"); +} + +#[test] +fn reject_multiple_rows_descending() { + let actual = nu!("[[a,b];[1 2] [3 4] [5 6]] | reject 2 1 | to nuon"); + assert_eq!(actual.out, "[[a, b]; [1, 2]]"); +}