Remove null elements from each row of a list array.
(
list_array: &GenericListArray<O>,
field: &Arc<arrow::datatypes::Field>,
)
| 126 | |
| 127 | /// Remove null elements from each row of a list array. |
| 128 | fn compact_list<O: OffsetSizeTrait>( |
| 129 | list_array: &GenericListArray<O>, |
| 130 | field: &Arc<arrow::datatypes::Field>, |
| 131 | ) -> Result<ArrayRef> { |
| 132 | let values = list_array.values(); |
| 133 | |
| 134 | // Fast path: no nulls in values, return input unchanged |
| 135 | if values.null_count() == 0 { |
| 136 | return Ok(Arc::new(list_array.clone())); |
| 137 | } |
| 138 | |
| 139 | let original_data = values.to_data(); |
| 140 | let capacity = original_data.len() - values.null_count(); |
| 141 | let mut offsets = Vec::<O>::with_capacity(list_array.len() + 1); |
| 142 | offsets.push(O::zero()); |
| 143 | let mut mutable = MutableArrayData::with_capacities( |
| 144 | vec![&original_data], |
| 145 | false, |
| 146 | Capacities::Array(capacity), |
| 147 | ); |
| 148 | |
| 149 | for row_index in 0..list_array.len() { |
| 150 | if list_array.nulls().is_some_and(|n| n.is_null(row_index)) { |
| 151 | offsets.push(offsets[row_index]); |
| 152 | continue; |
| 153 | } |
| 154 | |
| 155 | let start = list_array.offsets()[row_index].as_usize(); |
| 156 | let end = list_array.offsets()[row_index + 1].as_usize(); |
| 157 | let mut copied = 0usize; |
| 158 | |
| 159 | // Batch consecutive non-null elements into single extend() calls |
| 160 | // to reduce per-element overhead. For [1, 2, NULL, 3, 4] this |
| 161 | // produces 2 extend calls (0..2, 3..5) instead of 4 individual ones. |
| 162 | let mut batch_start: Option<usize> = None; |
| 163 | for i in start..end { |
| 164 | if values.is_null(i) { |
| 165 | // Null breaks the current batch — flush it |
| 166 | if let Some(bs) = batch_start { |
| 167 | mutable.extend(0, bs, i); |
| 168 | copied += i - bs; |
| 169 | batch_start = None; |
| 170 | } |
| 171 | } else if batch_start.is_none() { |
| 172 | batch_start = Some(i); |
| 173 | } |
| 174 | } |
| 175 | // Flush any remaining batch after the loop |
| 176 | if let Some(bs) = batch_start { |
| 177 | mutable.extend(0, bs, end); |
| 178 | copied += end - bs; |
| 179 | } |
| 180 | |
| 181 | offsets.push(offsets[row_index] + O::usize_as(copied)); |
| 182 | } |
| 183 | |
| 184 | let new_values = make_array(mutable.freeze()); |
| 185 | Ok(Arc::new(GenericListArray::<O>::try_new( |
nothing calls this directly
no test coverage detected
searching dependent graphs…