1use std::sync::Arc;
5
6use arrow_array::{Array, BooleanArray, GenericListArray, OffsetSizeTrait};
7use arrow_buffer::{BooleanBufferBuilder, OffsetBuffer, ScalarBuffer};
8use arrow_schema::Field;
9
10pub trait ListArrayExt {
11 fn filter_garbage_nulls(&self) -> Self;
19 fn trimmed_values(&self) -> Arc<dyn Array>;
26 type Offset: OffsetSizeTrait;
28 fn trimmed_offsets(&self) -> OffsetBuffer<Self::Offset>;
36}
37
38impl<OffsetSize: OffsetSizeTrait> ListArrayExt for GenericListArray<OffsetSize> {
39 fn filter_garbage_nulls(&self) -> Self {
40 if self.is_empty() {
41 return self.clone();
42 }
43 let Some(validity) = self.nulls().cloned() else {
44 return self.clone();
45 };
46
47 let mut should_keep = BooleanBufferBuilder::new(self.values().len());
48
49 let preamble_len = self.offsets().first().unwrap().to_usize().unwrap();
51 should_keep.append_n(preamble_len, false);
52
53 let mut new_offsets: Vec<OffsetSize> = Vec::with_capacity(self.len() + 1);
54 new_offsets.push(OffsetSize::zero());
55 let mut cur_len = OffsetSize::zero();
56 for (offset, is_valid) in self.offsets().windows(2).zip(validity.iter()) {
57 let len = offset[1] - offset[0];
58 if is_valid {
59 cur_len += len;
60 should_keep.append_n(len.to_usize().unwrap(), true);
61 new_offsets.push(cur_len);
62 } else {
63 should_keep.append_n(len.to_usize().unwrap(), false);
64 new_offsets.push(cur_len);
65 }
66 }
67
68 let trailer = self.values().len() - should_keep.len();
70 should_keep.append_n(trailer, false);
71
72 let should_keep = should_keep.finish();
73 let should_keep = BooleanArray::new(should_keep, None);
74 let new_values = arrow_select::filter::filter(self.values(), &should_keep).unwrap();
75 let new_offsets = ScalarBuffer::from(new_offsets);
76 let new_offsets = OffsetBuffer::new(new_offsets);
77
78 Self::new(
79 Arc::new(Field::new(
80 "item",
81 self.value_type(),
82 self.values().is_nullable(),
83 )),
84 new_offsets,
85 new_values,
86 Some(validity),
87 )
88 }
89
90 fn trimmed_values(&self) -> Arc<dyn Array> {
91 let first_value = self
92 .offsets()
93 .first()
94 .map(|v| v.to_usize().unwrap())
95 .unwrap_or(0);
96 let last_value = self
97 .offsets()
98 .last()
99 .map(|v| v.to_usize().unwrap())
100 .unwrap_or(0);
101 self.values().slice(first_value, last_value - first_value)
102 }
103
104 type Offset = OffsetSize;
105
106 fn trimmed_offsets(&self) -> OffsetBuffer<OffsetSize> {
107 let offsets = self.offsets();
108 let Some(&first) = offsets.first() else {
109 return offsets.clone();
110 };
111 if first == OffsetSize::zero() {
112 return offsets.clone();
113 }
114 let shifted: Vec<OffsetSize> = offsets.iter().map(|&o| o - first).collect();
115 OffsetBuffer::new(ScalarBuffer::from(shifted))
116 }
117}
118
119#[cfg(test)]
120mod tests {
121 use std::sync::Arc;
122
123 use arrow_array::{ListArray, UInt64Array};
124 use arrow_buffer::{BooleanBuffer, NullBuffer, OffsetBuffer, ScalarBuffer};
125 use arrow_schema::{DataType, Field};
126
127 use super::ListArrayExt;
128
129 #[test]
130 fn test_filter_garbage_nulls() {
131 let items = UInt64Array::from(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
132 let offsets = ScalarBuffer::<i32>::from(vec![2, 5, 8, 9]);
133 let offsets = OffsetBuffer::new(offsets);
134 let list_validity = NullBuffer::new(BooleanBuffer::from(vec![true, false, true]));
135 let list_arr = ListArray::new(
136 Arc::new(Field::new("item", DataType::UInt64, true)),
137 offsets,
138 Arc::new(items),
139 Some(list_validity.clone()),
140 );
141
142 let filtered = list_arr.filter_garbage_nulls();
143
144 let expected_items = UInt64Array::from(vec![2, 3, 4, 8]);
145 let offsets = ScalarBuffer::<i32>::from(vec![0, 3, 3, 4]);
146 let expected = ListArray::new(
147 Arc::new(Field::new("item", DataType::UInt64, false)),
148 OffsetBuffer::new(offsets),
149 Arc::new(expected_items),
150 Some(list_validity),
151 );
152
153 assert_eq!(filtered, expected);
154 }
155
156 #[test]
157 fn test_trim_values() {
158 let items = UInt64Array::from(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
159 let offsets = ScalarBuffer::<i32>::from(vec![2, 5, 6, 8, 9]);
160 let offsets = OffsetBuffer::new(offsets);
161 let list_arr = ListArray::new(
162 Arc::new(Field::new("item", DataType::UInt64, true)),
163 offsets,
164 Arc::new(items),
165 None,
166 );
167 let list_arr = list_arr.slice(1, 2);
168
169 let trimmed = list_arr.trimmed_values();
170
171 let expected_items = UInt64Array::from(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
172 let expected_items = expected_items.slice(5, 3);
173
174 assert_eq!(trimmed.as_ref(), &expected_items);
175 }
176}