use std::collections::HashMap;
use ndarray::ArrayViewD;
use super::Block;
use super::column::Column;
use super::column_view::ColumnView;
use super::dtype::DType;
use crate::types::{F, I, U};
pub struct BlockView<'a> {
map: HashMap<&'a str, ColumnView<'a>>,
nrows: Option<usize>,
}
impl<'a> BlockView<'a> {
pub fn new() -> Self {
Self {
map: HashMap::new(),
nrows: None,
}
}
pub fn insert(&mut self, key: &'a str, col: ColumnView<'a>) {
if self.nrows.is_none() {
self.nrows = col.nrows();
}
self.map.insert(key, col);
}
#[inline]
pub fn len(&self) -> usize {
self.map.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.map.is_empty()
}
#[inline]
pub fn nrows(&self) -> Option<usize> {
self.nrows
}
#[inline]
pub fn contains_key(&self, key: &str) -> bool {
self.map.contains_key(key)
}
#[inline]
pub fn get(&self, key: &str) -> Option<&ColumnView<'a>> {
self.map.get(key)
}
pub fn get_float(&self, key: &str) -> Option<ArrayViewD<'a, F>> {
self.get(key).and_then(|c| c.as_float())
}
pub fn get_int(&self, key: &str) -> Option<ArrayViewD<'a, I>> {
self.get(key).and_then(|c| c.as_int())
}
pub fn get_bool(&self, key: &str) -> Option<ArrayViewD<'a, bool>> {
self.get(key).and_then(|c| c.as_bool())
}
pub fn get_uint(&self, key: &str) -> Option<ArrayViewD<'a, U>> {
self.get(key).and_then(|c| c.as_uint())
}
pub fn get_u8(&self, key: &str) -> Option<ArrayViewD<'a, u8>> {
self.get(key).and_then(|c| c.as_u8())
}
pub fn get_string(&self, key: &str) -> Option<ArrayViewD<'a, String>> {
self.get(key).and_then(|c| c.as_string())
}
pub fn iter(&self) -> impl Iterator<Item = (&&'a str, &ColumnView<'a>)> {
self.map.iter()
}
pub fn keys(&self) -> impl Iterator<Item = &&'a str> {
self.map.keys()
}
pub fn values(&self) -> impl Iterator<Item = &ColumnView<'a>> {
self.map.values()
}
pub fn dtype(&self, key: &str) -> Option<DType> {
self.get(key).map(|c| c.dtype())
}
pub fn to_owned(&self) -> Block {
let mut block = Block::new();
for (&key, col_view) in &self.map {
let col: Column = col_view.to_owned();
let _ = block.insert_column(key, col);
}
block
}
}
impl<'a> Default for BlockView<'a> {
fn default() -> Self {
Self::new()
}
}
impl<'a> From<&'a Block> for BlockView<'a> {
fn from(block: &'a Block) -> Self {
let mut view = BlockView {
map: HashMap::with_capacity(block.len()),
nrows: block.nrows(),
};
for (key, col) in block.iter() {
view.map.insert(key, ColumnView::from(col));
}
view
}
}
impl std::fmt::Debug for BlockView<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut map = f.debug_map();
for (k, v) in &self.map {
map.entry(k, &format!("{}(shape={:?})", v.dtype(), v.shape()));
}
map.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
use ndarray::Array1;
#[test]
fn test_from_block() {
let mut block = Block::new();
block
.insert("x", Array1::from_vec(vec![1.0 as F, 2.0, 3.0]).into_dyn())
.unwrap();
block
.insert("id", Array1::from_vec(vec![10 as I, 20, 30]).into_dyn())
.unwrap();
let view = BlockView::from(&block);
assert_eq!(view.len(), 2);
assert_eq!(view.nrows(), Some(3));
assert!(view.contains_key("x"));
assert!(view.contains_key("id"));
assert!(!view.is_empty());
}
#[test]
fn test_typed_getters() {
let mut block = Block::new();
block
.insert("x", Array1::from_vec(vec![1.0 as F, 2.0, 3.0]).into_dyn())
.unwrap();
block
.insert("id", Array1::from_vec(vec![10 as I, 20, 30]).into_dyn())
.unwrap();
let view = BlockView::from(&block);
assert!(view.get_float("x").is_some());
assert!(view.get_int("id").is_some());
assert!(view.get_int("x").is_none());
assert!(view.get_float("id").is_none());
assert!(view.get_float("missing").is_none());
}
#[test]
fn test_to_owned_roundtrip() {
let mut block = Block::new();
block
.insert("x", Array1::from_vec(vec![1.0 as F, 2.0, 3.0]).into_dyn())
.unwrap();
block
.insert("id", Array1::from_vec(vec![10 as I, 20, 30]).into_dyn())
.unwrap();
let view = BlockView::from(&block);
let owned = view.to_owned();
assert_eq!(owned.nrows(), Some(3));
assert_eq!(owned.len(), 2);
assert_eq!(
owned
.get_float("x")
.unwrap()
.as_slice_memory_order()
.unwrap(),
&[1.0, 2.0, 3.0]
);
assert_eq!(
owned
.get_int("id")
.unwrap()
.as_slice_memory_order()
.unwrap(),
&[10, 20, 30]
);
}
#[test]
fn test_zero_copy() {
let mut block = Block::new();
block
.insert("x", Array1::from_vec(vec![1.0 as F, 2.0, 3.0]).into_dyn())
.unwrap();
let view = BlockView::from(&block);
let orig_ptr = block.get_float("x").unwrap().as_ptr();
let view_ptr = view.get_float("x").unwrap().as_ptr();
assert_eq!(orig_ptr, view_ptr);
}
#[test]
fn test_empty_view() {
let view = BlockView::new();
assert!(view.is_empty());
assert_eq!(view.len(), 0);
assert_eq!(view.nrows(), None);
}
#[test]
fn test_iter_keys() {
let mut block = Block::new();
block
.insert("x", Array1::from_vec(vec![1.0 as F, 2.0]).into_dyn())
.unwrap();
block
.insert("id", Array1::from_vec(vec![10 as I, 20]).into_dyn())
.unwrap();
let view = BlockView::from(&block);
let keys: Vec<&&str> = view.keys().collect();
assert_eq!(keys.len(), 2);
}
#[test]
fn test_dtype_query() {
let mut block = Block::new();
block
.insert("x", Array1::from_vec(vec![1.0 as F]).into_dyn())
.unwrap();
let view = BlockView::from(&block);
assert_eq!(view.dtype("x"), Some(DType::Float));
assert_eq!(view.dtype("missing"), None);
}
}