molrs_core/block/
block_view.rs1use std::collections::HashMap;
8
9use ndarray::ArrayViewD;
10
11use super::Block;
12use super::column::Column;
13use super::column_view::ColumnView;
14use super::dtype::DType;
15use crate::types::{F, I, U};
16
17pub struct BlockView<'a> {
22 map: HashMap<&'a str, ColumnView<'a>>,
23 nrows: Option<usize>,
24}
25
26impl<'a> BlockView<'a> {
27 pub fn new() -> Self {
29 Self {
30 map: HashMap::new(),
31 nrows: None,
32 }
33 }
34
35 pub fn insert(&mut self, key: &'a str, col: ColumnView<'a>) {
41 if self.nrows.is_none() {
42 self.nrows = col.nrows();
43 }
44 self.map.insert(key, col);
45 }
46
47 #[inline]
49 pub fn len(&self) -> usize {
50 self.map.len()
51 }
52
53 #[inline]
55 pub fn is_empty(&self) -> bool {
56 self.map.is_empty()
57 }
58
59 #[inline]
61 pub fn nrows(&self) -> Option<usize> {
62 self.nrows
63 }
64
65 #[inline]
67 pub fn contains_key(&self, key: &str) -> bool {
68 self.map.contains_key(key)
69 }
70
71 #[inline]
73 pub fn get(&self, key: &str) -> Option<&ColumnView<'a>> {
74 self.map.get(key)
75 }
76
77 pub fn get_float(&self, key: &str) -> Option<ArrayViewD<'a, F>> {
79 self.get(key).and_then(|c| c.as_float())
80 }
81
82 pub fn get_int(&self, key: &str) -> Option<ArrayViewD<'a, I>> {
84 self.get(key).and_then(|c| c.as_int())
85 }
86
87 pub fn get_bool(&self, key: &str) -> Option<ArrayViewD<'a, bool>> {
89 self.get(key).and_then(|c| c.as_bool())
90 }
91
92 pub fn get_uint(&self, key: &str) -> Option<ArrayViewD<'a, U>> {
94 self.get(key).and_then(|c| c.as_uint())
95 }
96
97 pub fn get_u8(&self, key: &str) -> Option<ArrayViewD<'a, u8>> {
99 self.get(key).and_then(|c| c.as_u8())
100 }
101
102 pub fn get_string(&self, key: &str) -> Option<ArrayViewD<'a, String>> {
104 self.get(key).and_then(|c| c.as_string())
105 }
106
107 pub fn iter(&self) -> impl Iterator<Item = (&&'a str, &ColumnView<'a>)> {
109 self.map.iter()
110 }
111
112 pub fn keys(&self) -> impl Iterator<Item = &&'a str> {
114 self.map.keys()
115 }
116
117 pub fn values(&self) -> impl Iterator<Item = &ColumnView<'a>> {
119 self.map.values()
120 }
121
122 pub fn dtype(&self, key: &str) -> Option<DType> {
124 self.get(key).map(|c| c.dtype())
125 }
126
127 pub fn to_owned(&self) -> Block {
129 let mut block = Block::new();
130 for (&key, col_view) in &self.map {
131 let col: Column = col_view.to_owned();
132 let _ = block.insert_column(key, col);
133 }
134 block
135 }
136}
137
138impl<'a> Default for BlockView<'a> {
139 fn default() -> Self {
140 Self::new()
141 }
142}
143
144impl<'a> From<&'a Block> for BlockView<'a> {
145 fn from(block: &'a Block) -> Self {
146 let mut view = BlockView {
147 map: HashMap::with_capacity(block.len()),
148 nrows: block.nrows(),
149 };
150 for (key, col) in block.iter() {
151 view.map.insert(key, ColumnView::from(col));
152 }
153 view
154 }
155}
156
157impl std::fmt::Debug for BlockView<'_> {
158 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
159 let mut map = f.debug_map();
160 for (k, v) in &self.map {
161 map.entry(k, &format!("{}(shape={:?})", v.dtype(), v.shape()));
162 }
163 map.finish()
164 }
165}
166
167#[cfg(test)]
168mod tests {
169 use super::*;
170 use ndarray::Array1;
171
172 #[test]
173 fn test_from_block() {
174 let mut block = Block::new();
175 block
176 .insert("x", Array1::from_vec(vec![1.0 as F, 2.0, 3.0]).into_dyn())
177 .unwrap();
178 block
179 .insert("id", Array1::from_vec(vec![10 as I, 20, 30]).into_dyn())
180 .unwrap();
181
182 let view = BlockView::from(&block);
183 assert_eq!(view.len(), 2);
184 assert_eq!(view.nrows(), Some(3));
185 assert!(view.contains_key("x"));
186 assert!(view.contains_key("id"));
187 assert!(!view.is_empty());
188 }
189
190 #[test]
191 fn test_typed_getters() {
192 let mut block = Block::new();
193 block
194 .insert("x", Array1::from_vec(vec![1.0 as F, 2.0, 3.0]).into_dyn())
195 .unwrap();
196 block
197 .insert("id", Array1::from_vec(vec![10 as I, 20, 30]).into_dyn())
198 .unwrap();
199
200 let view = BlockView::from(&block);
201
202 assert!(view.get_float("x").is_some());
204 assert!(view.get_int("id").is_some());
205
206 assert!(view.get_int("x").is_none());
208 assert!(view.get_float("id").is_none());
209
210 assert!(view.get_float("missing").is_none());
212 }
213
214 #[test]
215 fn test_to_owned_roundtrip() {
216 let mut block = Block::new();
217 block
218 .insert("x", Array1::from_vec(vec![1.0 as F, 2.0, 3.0]).into_dyn())
219 .unwrap();
220 block
221 .insert("id", Array1::from_vec(vec![10 as I, 20, 30]).into_dyn())
222 .unwrap();
223
224 let view = BlockView::from(&block);
225 let owned = view.to_owned();
226
227 assert_eq!(owned.nrows(), Some(3));
228 assert_eq!(owned.len(), 2);
229 assert_eq!(
230 owned
231 .get_float("x")
232 .unwrap()
233 .as_slice_memory_order()
234 .unwrap(),
235 &[1.0, 2.0, 3.0]
236 );
237 assert_eq!(
238 owned
239 .get_int("id")
240 .unwrap()
241 .as_slice_memory_order()
242 .unwrap(),
243 &[10, 20, 30]
244 );
245 }
246
247 #[test]
248 fn test_zero_copy() {
249 let mut block = Block::new();
250 block
251 .insert("x", Array1::from_vec(vec![1.0 as F, 2.0, 3.0]).into_dyn())
252 .unwrap();
253
254 let view = BlockView::from(&block);
255 let orig_ptr = block.get_float("x").unwrap().as_ptr();
256 let view_ptr = view.get_float("x").unwrap().as_ptr();
257 assert_eq!(orig_ptr, view_ptr);
258 }
259
260 #[test]
261 fn test_empty_view() {
262 let view = BlockView::new();
263 assert!(view.is_empty());
264 assert_eq!(view.len(), 0);
265 assert_eq!(view.nrows(), None);
266 }
267
268 #[test]
269 fn test_iter_keys() {
270 let mut block = Block::new();
271 block
272 .insert("x", Array1::from_vec(vec![1.0 as F, 2.0]).into_dyn())
273 .unwrap();
274 block
275 .insert("id", Array1::from_vec(vec![10 as I, 20]).into_dyn())
276 .unwrap();
277
278 let view = BlockView::from(&block);
279 let keys: Vec<&&str> = view.keys().collect();
280 assert_eq!(keys.len(), 2);
281 }
282
283 #[test]
284 fn test_dtype_query() {
285 let mut block = Block::new();
286 block
287 .insert("x", Array1::from_vec(vec![1.0 as F]).into_dyn())
288 .unwrap();
289
290 let view = BlockView::from(&block);
291 assert_eq!(view.dtype("x"), Some(DType::Float));
292 assert_eq!(view.dtype("missing"), None);
293 }
294}