nu_command/matrix/
into_nu.rs1use crate::matrix::MatrixValue;
2use nu_engine::command_prelude::*;
3
4#[derive(Clone)]
5pub struct MatrixIntoNu;
6
7impl Command for MatrixIntoNu {
8 fn name(&self) -> &str {
9 "matrix into-nu"
10 }
11
12 fn signature(&self) -> Signature {
13 Signature::build("matrix into-nu")
14 .input_output_types(vec![(Type::Custom("matrix".into()), Type::table())])
15 .switch(
16 "as-records",
17 "Output as a list of records with auto-generated column names",
18 Some('r'),
19 )
20 .category(Category::Conversions)
21 }
22
23 fn description(&self) -> &str {
24 "Convert a matrix to a nushell table (list of lists by default)."
25 }
26
27 fn search_terms(&self) -> Vec<&str> {
28 vec!["convert", "table", "list"]
29 }
30
31 fn run(
32 &self,
33 engine_state: &EngineState,
34 stack: &mut Stack,
35 call: &Call,
36 input: PipelineData,
37 ) -> Result<PipelineData, ShellError> {
38 let head = call.head;
39 let as_records = call.has_flag(engine_state, stack, "as-records")?;
40 let matrix = MatrixValue::from_value(&input.into_value(head)?)?;
41
42 if matrix.array.ndim() == 0 {
43 return Ok(
44 Value::float(matrix.array.first().copied().unwrap_or(0.0), head)
45 .into_pipeline_data(),
46 );
47 }
48
49 if as_records {
50 matrix_to_records(&matrix.array, head)
51 } else {
52 matrix_to_lists(&matrix.array, head)
53 }
54 }
55
56 fn examples(&self) -> Vec<Example<'static>> {
57 vec![
58 Example {
59 description: "Convert an identity matrix to a nushell table",
60 example: "matrix identity 2 | matrix into-nu | to nuon",
61 result: Some(Value::test_string("[[1.0, 0.0], [0.0, 1.0]]")),
62 },
63 Example {
64 description: "Convert a matrix to records",
65 example: "matrix identity 2 | matrix into-nu --as-records | to nuon",
66 result: Some(Value::test_string(
67 "[[\"col0\", \"col1\"]; [1.0, 0.0], [0.0, 1.0]]",
68 )),
69 },
70 ]
71 }
72}
73
74fn matrix_to_lists(array: &ndarray::ArrayD<f64>, span: Span) -> Result<PipelineData, ShellError> {
75 let rows = match array.ndim() {
76 0 => {
77 vec![Value::float(array.first().copied().unwrap_or(0.0), span)]
78 }
79 1 => {
80 let vals: Vec<Value> = array.iter().map(|v| Value::float(*v, span)).collect();
81 vec![Value::list(vals, span)]
82 }
83 2 => array
84 .axis_iter(ndarray::Axis(0))
85 .map(|row| {
86 let vals: Vec<Value> = row.iter().map(|v| Value::float(*v, span)).collect();
87 Value::list(vals, span)
88 })
89 .collect(),
90 _ => array
91 .axis_iter(ndarray::Axis(0))
92 .map(|sub| {
93 let sub_lists: Vec<Value> = sub
94 .axis_iter(ndarray::Axis(0))
95 .map(|inner| {
96 let vals: Vec<Value> =
97 inner.iter().map(|v| Value::float(*v, span)).collect();
98 Value::list(vals, span)
99 })
100 .collect();
101 Value::list(sub_lists, span)
102 })
103 .collect(),
104 };
105
106 Ok(Value::list(rows, span).into_pipeline_data())
107}
108
109fn matrix_to_records(array: &ndarray::ArrayD<f64>, span: Span) -> Result<PipelineData, ShellError> {
110 let ncols = if array.ndim() >= 2 {
111 array.shape()[array.ndim() - 1]
112 } else {
113 array.len()
114 };
115 let col_names: Vec<String> = (0..ncols).map(|i| format!("col{}", i)).collect();
116
117 let rows: Vec<Value> = array
118 .axis_iter(ndarray::Axis(0))
119 .map(|row| {
120 let mut record = nu_protocol::Record::new();
121 for (j, name) in col_names.iter().enumerate() {
122 let val = if j < row.len() {
123 if let Some(&v) = row.get(j) {
124 Value::float(v, span)
125 } else {
126 Value::float(0.0, span)
127 }
128 } else {
129 Value::float(0.0, span)
130 };
131 record.push(name, val);
132 }
133 Value::record(record, span)
134 })
135 .collect();
136
137 Ok(Value::list(rows, span).into_pipeline_data())
138}