diff --git a/src/structs/table.rs b/src/structs/table.rs index c092e23..b4a92c2 100644 --- a/src/structs/table.rs +++ b/src/structs/table.rs @@ -32,7 +32,7 @@ use crate::Field; #[cfg(feature = "chunked")] use crate::SuperTable; #[cfg(feature = "views")] -use crate::TableV; +use crate::{BitmaskV, NumericArrayV, TableV, TextArrayV}; use crate::enums::{error::MinarrowError, shape_dim::ShapeDim}; #[cfg(feature = "chunked")] use crate::traits::consolidate::Consolidate; @@ -204,6 +204,33 @@ impl Table { self.cols.iter().position(|fa| fa.field.name == name) } + /// Resolve a named column to a `NumericArrayV`. + #[cfg(feature = "views")] + pub fn col_numeric(&self, name: &str) -> Result { + let idx = self.col_name_index(name) + .ok_or_else(|| MinarrowError::IndexError(format!("column '{}' not found", name)))?; + let num = self.cols[idx].array.num_ref()?; + Ok(NumericArrayV::from(num.clone())) + } + + /// Resolve a named column to a `TextArrayV`. + #[cfg(feature = "views")] + pub fn col_text(&self, name: &str) -> Result { + let idx = self.col_name_index(name) + .ok_or_else(|| MinarrowError::IndexError(format!("column '{}' not found", name)))?; + let ta = self.cols[idx].array.str_ref()?; + Ok(TextArrayV::from(ta.clone())) + } + + /// Resolve a named column to a `BitmaskV`. + #[cfg(feature = "views")] + pub fn col_bitmask(&self, name: &str) -> Result { + let idx = self.col_name_index(name) + .ok_or_else(|| MinarrowError::IndexError(format!("column '{}' not found", name)))?; + let ba = self.cols[idx].array.bool_ref()?; + Ok(BitmaskV::new(ba.data.clone(), 0, ba.len)) + } + /// Removes a column by name. pub fn remove_col(&mut self, name: &str) -> bool { if let Some(idx) = self.col_name_index(name) { @@ -367,6 +394,24 @@ impl Table { self.cols.iter().map(func).collect() } + /// Apply a transformation to each column, producing a new table. + /// + /// The closure receives each FieldArray and returns a transformed FieldArray. + /// To pass a column through unchanged, clone it. The closure can dispatch + /// on `Array` variant to handle numeric, text, temporal, and boolean columns + /// differently. + pub fn apply_cols( + &self, + mut f: impl FnMut(&FieldArray) -> Result, + ) -> Result { + let cols = self + .cols + .iter() + .map(|fa| f(fa)) + .collect::, E>>()?; + Ok(Table::new(self.name.clone(), Some(cols))) + } + /// Inserts rows from another table at the specified index. /// /// This is an **O(n)** operation where n is the number of rows after the insertion point. diff --git a/src/structs/views/table_view.rs b/src/structs/views/table_view.rs index eab180a..81ecfa5 100644 --- a/src/structs/views/table_view.rs +++ b/src/structs/views/table_view.rs @@ -269,6 +269,25 @@ impl TableV { } } + /// Apply a transformation to each column view, producing a new table. + /// + /// The closure receives the field and column view directly without + /// materialisation. To pass a column through unchanged, materialise + /// with `view.to_array()`. To transform, operate on the view and + /// return the result as a FieldArray. + pub fn apply_cols( + &self, + mut f: impl FnMut(&Arc, &ArrayV) -> Result, + ) -> Result { + let cols = self + .fields + .iter() + .zip(self.cols.iter()) + .map(|(field, col_view)| f(field, col_view)) + .collect::, E>>()?; + Ok(Table::new(self.name.clone(), Some(cols))) + } + /// Gather specific rows from an ArrayV window #[cfg(feature = "select")] fn gather_rows_from_window(&self, window: &ArrayV, row_indices: &[usize]) -> Option {