Skip to content

Commit b4038fe

Browse files
authored
compact works on records too (nushell#16810)
1 parent 46787d7 commit b4038fe

File tree

1 file changed

+45
-69
lines changed

1 file changed

+45
-69
lines changed

crates/nu-command/src/filters/compact.rs

Lines changed: 45 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ impl Command for Compact {
1010

1111
fn signature(&self) -> Signature {
1212
Signature::build("compact")
13-
.input_output_types(vec![(
14-
Type::List(Box::new(Type::Any)),
15-
Type::List(Box::new(Type::Any)),
16-
)])
13+
.input_output_types(vec![
14+
(Type::record(), Type::record()),
15+
(Type::list(Type::Any), Type::list(Type::Any)),
16+
])
1717
.switch(
1818
"empty",
1919
"also compact empty items like \"\", {}, and []",
@@ -31,6 +31,10 @@ impl Command for Compact {
3131
"Creates a table with non-empty rows."
3232
}
3333

34+
fn extra_description(&self) -> &str {
35+
"Can be used to remove `null` or empty values from lists and records too."
36+
}
37+
3438
fn search_terms(&self) -> Vec<&str> {
3539
vec!["empty", "remove"]
3640
}
@@ -40,10 +44,21 @@ impl Command for Compact {
4044
engine_state: &EngineState,
4145
stack: &mut Stack,
4246
call: &Call,
43-
input: PipelineData,
47+
mut input: PipelineData,
4448
) -> Result<PipelineData, ShellError> {
4549
let empty = call.has_flag(engine_state, stack, "empty")?;
46-
compact(engine_state, stack, call, input, empty)
50+
let columns: Vec<String> = call.rest(engine_state, stack, 0)?;
51+
52+
match input {
53+
PipelineData::Value(Value::Record { ref mut val, .. }, ..) => {
54+
val.to_mut().retain(|_, val| do_keep_value(val, empty));
55+
Ok(input)
56+
}
57+
_ => input.filter(
58+
move |item| do_keep_row(item, empty, columns.as_slice()),
59+
engine_state.signals(),
60+
),
61+
}
4762
}
4863

4964
fn examples(&self) -> Vec<Example<'_>> {
@@ -80,74 +95,35 @@ impl Command for Compact {
8095
Value::test_int(5),
8196
])),
8297
},
98+
Example {
99+
description: "Filter out all instances of null from a record",
100+
example: r#"{a: 1, b: null, c: 3} | compact"#,
101+
result: Some(Value::test_record(record! {
102+
"a" => Value::test_int(1),
103+
"c" => Value::test_int(3),
104+
})),
105+
},
83106
]
84107
}
85108
}
86109

87-
pub fn compact(
88-
engine_state: &EngineState,
89-
stack: &mut Stack,
90-
call: &Call,
91-
input: PipelineData,
92-
compact_empties: bool,
93-
) -> Result<PipelineData, ShellError> {
94-
let columns: Vec<String> = call.rest(engine_state, stack, 0)?;
95-
let metadata = input.metadata();
96-
input
97-
.filter(
98-
move |item| {
99-
match item {
100-
// Nothing is filtered out
101-
Value::Nothing { .. } => false,
102-
Value::Record { val, .. } => {
103-
for column in columns.iter() {
104-
match val.get(column) {
105-
None => return false,
106-
Some(x) => {
107-
if let Value::Nothing { .. } = x {
108-
return false;
109-
}
110-
if compact_empties {
111-
// check if the value is one of the empty value
112-
if match x {
113-
Value::String { val, .. } => val.is_empty(),
114-
Value::Record { val, .. } => val.is_empty(),
115-
Value::List { vals, .. } => vals.is_empty(),
116-
_ => false,
117-
} {
118-
// one of the empty value found so skip now
119-
return false;
120-
}
121-
}
122-
}
123-
}
124-
}
110+
fn do_keep_value(value: &Value, compact_empties: bool) -> bool {
111+
let remove = match compact_empties {
112+
true => value.is_empty(),
113+
false => value.is_nothing(),
114+
};
115+
!remove
116+
}
117+
118+
fn do_keep_row(row: &Value, compact_empties: bool, columns: &[impl AsRef<str>]) -> bool {
119+
let do_keep = move |value| do_keep_value(value, compact_empties);
125120

126-
if compact_empties && val.is_empty() {
127-
return false;
128-
}
129-
// No defined columns contained Nothing
130-
true
131-
}
132-
Value::List { vals, .. } => {
133-
if compact_empties && vals.is_empty() {
134-
return false;
135-
}
136-
true
137-
}
138-
Value::String { val, .. } => {
139-
if compact_empties && val.is_empty() {
140-
return false;
141-
}
142-
true
143-
}
144-
// Any non-Nothing, non-record should be kept
145-
_ => true,
146-
}
147-
},
148-
engine_state.signals(),
149-
)
150-
.map(|m| m.set_metadata(metadata))
121+
do_keep(row)
122+
&& row.as_record().map_or(true, |record| {
123+
columns
124+
.iter()
125+
.all(|col| record.get(col).map(do_keep).unwrap_or(false))
126+
})
151127
}
152128

153129
#[cfg(test)]

0 commit comments

Comments
 (0)