nu_command/filters/
compact.rs

1use nu_engine::command_prelude::*;
2
3#[derive(Clone)]
4pub struct Compact;
5
6impl Command for Compact {
7    fn name(&self) -> &str {
8        "compact"
9    }
10
11    fn signature(&self) -> Signature {
12        Signature::build("compact")
13            .input_output_types(vec![(
14                Type::List(Box::new(Type::Any)),
15                Type::List(Box::new(Type::Any)),
16            )])
17            .switch(
18                "empty",
19                "also compact empty items like \"\", {}, and []",
20                Some('e'),
21            )
22            .rest(
23                "columns",
24                SyntaxShape::Any,
25                "The columns to compact from the table.",
26            )
27            .category(Category::Filters)
28    }
29
30    fn description(&self) -> &str {
31        "Creates a table with non-empty rows."
32    }
33
34    fn search_terms(&self) -> Vec<&str> {
35        vec!["empty", "remove"]
36    }
37
38    fn run(
39        &self,
40        engine_state: &EngineState,
41        stack: &mut Stack,
42        call: &Call,
43        input: PipelineData,
44    ) -> Result<PipelineData, ShellError> {
45        let empty = call.has_flag(engine_state, stack, "empty")?;
46        compact(engine_state, stack, call, input, empty)
47    }
48
49    fn examples(&self) -> Vec<Example> {
50        vec![
51            Example {
52                description: "Filter out all records where 'Hello' is null",
53                example: r#"[["Hello" "World"]; [null 3]] | compact Hello"#,
54                result: Some(Value::test_list(vec![])),
55            },
56            Example {
57                description: "Filter out all records where 'World' is null",
58                example: r#"[["Hello" "World"]; [null 3]] | compact World"#,
59                result: Some(Value::test_list(vec![Value::test_record(record! {
60                    "Hello" => Value::nothing(Span::test_data()),
61                    "World" => Value::test_int(3),
62                })])),
63            },
64            Example {
65                description: "Filter out all instances of null from a list",
66                example: r#"[1, null, 2] | compact"#,
67                result: Some(Value::test_list(vec![
68                    Value::test_int(1),
69                    Value::test_int(2),
70                ])),
71            },
72            Example {
73                description: "Filter out all instances of null and empty items from a list",
74                example: r#"[1, null, 2, "", 3, [], 4, {}, 5] | compact --empty"#,
75                result: Some(Value::test_list(vec![
76                    Value::test_int(1),
77                    Value::test_int(2),
78                    Value::test_int(3),
79                    Value::test_int(4),
80                    Value::test_int(5),
81                ])),
82            },
83        ]
84    }
85}
86
87pub 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                        }
125
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))
151}
152
153#[cfg(test)]
154mod tests {
155    use super::Compact;
156
157    #[test]
158    fn examples_work_as_expected() {
159        use crate::test_examples;
160        test_examples(Compact {})
161    }
162}