vortex_array/arrays/dict/compute/
slice.rs1use std::ops::Range;
5
6use vortex_error::VortexExpect;
7use vortex_error::VortexResult;
8
9use crate::ArrayRef;
10use crate::IntoArray;
11use crate::array::ArrayView;
12use crate::arrays::Constant;
13use crate::arrays::ConstantArray;
14use crate::arrays::Dict;
15use crate::arrays::DictArray;
16use crate::arrays::Primitive;
17use crate::arrays::dict::DictArraySlotsExt;
18use crate::arrays::slice::SliceReduce;
19use crate::expr::stats::Precision;
20use crate::expr::stats::Stat;
21use crate::scalar::Scalar;
22use crate::scalar::ScalarValue;
23
24impl SliceReduce for Dict {
25 fn slice(array: ArrayView<'_, Self>, range: Range<usize>) -> VortexResult<Option<ArrayRef>> {
26 if let Some(code) = array.codes().as_opt::<Constant>() {
27 return slice_constant_code(array, code.scalar(), range.len());
28 }
29
30 let sliced_code = if let Some(codes) = array.codes().as_typed::<Primitive>() {
31 let sliced_code = <Primitive as SliceReduce>::slice(codes, range)?
32 .vortex_expect("Primitive SliceReduce should always return Some");
33 inherit_slice_stats(array.codes(), &sliced_code);
35 sliced_code
36 } else {
37 array.codes().slice(range)?
38 };
39
40 if let Some(code) = sliced_code.as_opt::<Constant>() {
42 return slice_constant_code(array, code.scalar(), sliced_code.len());
43 }
44 let array =
46 unsafe { DictArray::new_unchecked(sliced_code, array.values().clone()).into_array() };
47
48 Ok(Some(array))
49 }
50}
51
52fn inherit_slice_stats(source: &ArrayRef, sliced: &ArrayRef) {
53 source.statistics().with_iter(|iter| {
54 sliced
55 .statistics()
56 .inherit(iter.filter(|(stat, value)| is_inheritable_true_slice_stat(*stat, value)));
57 });
58}
59
60fn is_inheritable_true_slice_stat(stat: Stat, value: &Precision<ScalarValue>) -> bool {
61 matches!(
62 stat,
63 Stat::IsConstant | Stat::IsSorted | Stat::IsStrictSorted
64 ) && value
65 .as_ref()
66 .as_exact()
67 .is_some_and(|value| matches!(value, ScalarValue::Bool(true)))
68}
69
70fn slice_constant_code(
71 array: ArrayView<'_, Dict>,
72 code: &Scalar,
73 len: usize,
74) -> VortexResult<Option<ArrayRef>> {
75 let code = code.as_primitive().as_::<usize>();
76 if let Some(code) = code {
77 let values = array.values().slice(code..code + 1)?;
78 Ok(Some(
79 DictArray::new(ConstantArray::new(0u8, len).into_array(), values).into_array(),
80 ))
81 } else {
82 Ok(Some(
83 ConstantArray::new(Scalar::null(array.dtype().clone()), len).into_array(),
84 ))
85 }
86}
87
88#[cfg(test)]
89mod tests {
90 use vortex_buffer::buffer;
91 use vortex_error::VortexResult;
92
93 use crate::IntoArray;
94 use crate::arrays::DictArray;
95 use crate::arrays::PrimitiveArray;
96 use crate::arrays::dict::compute::slice::ConstantArray;
97 use crate::assert_arrays_eq;
98 use crate::dtype::DType;
99 use crate::dtype::Nullability::Nullable;
100 use crate::dtype::PType;
101 use crate::scalar::Scalar;
102
103 #[test]
104 fn slice_constant_valid_code() -> VortexResult<()> {
105 let dict = DictArray::new(
106 ConstantArray::new(1u8, 5).into_array(),
107 buffer![10i32, 20, 30].into_array(),
108 );
109 let sliced = dict.slice(1..4)?;
110 let expected = PrimitiveArray::from_iter([20i32, 20, 20]).into_array();
111 assert_arrays_eq!(sliced, expected);
112 Ok(())
113 }
114
115 #[test]
116 fn slice_constant_null_code() -> VortexResult<()> {
117 let dict = DictArray::new(
118 ConstantArray::new(Scalar::null(DType::Primitive(PType::U8, Nullable)), 5).into_array(),
119 buffer![10i32, 20, 30].into_array(),
120 );
121 let sliced = dict.slice(1..4)?;
122 let expected =
123 PrimitiveArray::from_option_iter([Option::<i32>::None, None, None]).into_array();
124 assert_arrays_eq!(sliced, expected);
125 Ok(())
126 }
127}