1use vortex_error::VortexExpect;
5use vortex_error::VortexResult;
6
7use crate::ArrayRef;
8use crate::IntoArray;
9use crate::array::ArrayView;
10use crate::arrays::List;
11use crate::arrays::ListArray;
12use crate::arrays::Primitive;
13use crate::arrays::PrimitiveArray;
14use crate::arrays::dict::TakeExecute;
15use crate::arrays::list::ListArrayExt;
16use crate::arrays::primitive::PrimitiveArrayExt;
17use crate::builders::ArrayBuilder;
18use crate::builders::PrimitiveBuilder;
19use crate::dtype::IntegerPType;
20use crate::dtype::Nullability;
21use crate::executor::ExecutionCtx;
22use crate::match_each_unsigned_integer_ptype;
23use crate::match_smallest_offset_type;
24
25impl TakeExecute for List {
29 #[expect(clippy::cognitive_complexity)]
35 fn take(
36 array: ArrayView<'_, List>,
37 indices: &ArrayRef,
38 ctx: &mut ExecutionCtx,
39 ) -> VortexResult<Option<ArrayRef>> {
40 let indices = indices.clone().execute::<PrimitiveArray>(ctx)?;
41 let indices = indices.reinterpret_cast(indices.ptype().to_unsigned());
42 let offsets = array.offsets().clone().execute::<PrimitiveArray>(ctx)?;
43 let offsets = offsets.reinterpret_cast(offsets.ptype().to_unsigned());
44 let total_approx = array.elements().len().saturating_mul(indices.len());
46
47 match_each_unsigned_integer_ptype!(offsets.ptype(), |O| {
48 match_each_unsigned_integer_ptype!(indices.ptype(), |I| {
49 match_smallest_offset_type!(total_approx, |OutputOffsetType| {
50 _take::<I, O, OutputOffsetType>(
51 array,
52 offsets.as_view(),
53 indices.as_view(),
54 ctx,
55 )
56 .map(Some)
57 })
58 })
59 })
60 }
61}
62
63fn _take<I: IntegerPType, O: IntegerPType, OutputOffsetType: IntegerPType>(
64 array: ArrayView<'_, List>,
65 offsets_array: ArrayView<'_, Primitive>,
66 indices_array: ArrayView<'_, Primitive>,
67 ctx: &mut ExecutionCtx,
68) -> VortexResult<ArrayRef> {
69 let data_validity = array
70 .list_validity()
71 .execute_mask(array.as_ref().len(), ctx)?;
72 let indices_validity = indices_array
73 .validity()
74 .vortex_expect("Failed to compute validity mask")
75 .execute_mask(indices_array.as_ref().len(), ctx)?;
76
77 if !indices_validity.all_true() || !data_validity.all_true() {
78 return _take_nullable::<I, O, OutputOffsetType>(array, offsets_array, indices_array, ctx);
79 }
80
81 let offsets: &[O] = offsets_array.as_slice();
82 let indices: &[I] = indices_array.as_slice();
83
84 let mut new_offsets = PrimitiveBuilder::<OutputOffsetType>::with_capacity(
85 Nullability::NonNullable,
86 indices.len(),
87 );
88 let mut elements_to_take =
89 PrimitiveBuilder::with_capacity(Nullability::NonNullable, 2 * indices.len());
90
91 let mut current_offset = OutputOffsetType::zero();
92 new_offsets.append_zero();
93
94 for &data_idx in indices {
95 let data_idx: usize = data_idx.as_();
96
97 let start = offsets[data_idx];
98 let stop = offsets[data_idx + 1];
99
100 let additional: usize = (stop - start).as_();
106
107 elements_to_take.reserve_exact(additional);
109 for i in 0..additional {
110 elements_to_take.append_value(start + O::from_usize(i).vortex_expect("i < additional"));
111 }
112 current_offset +=
113 OutputOffsetType::from_usize((stop - start).as_()).vortex_expect("offset conversion");
114 new_offsets.append_value(current_offset);
115 }
116
117 let elements_to_take = elements_to_take.finish();
118 let new_offsets = new_offsets.finish();
119
120 let new_elements = array.elements().take(elements_to_take)?;
121
122 Ok(ListArray::try_new(
123 new_elements,
124 new_offsets,
125 array.validity()?.take(indices_array.array())?,
126 )?
127 .into_array())
128}
129
130#[inline(never)]
133fn _take_nullable<I: IntegerPType, O: IntegerPType, OutputOffsetType: IntegerPType>(
134 array: ArrayView<'_, List>,
135 offsets_array: ArrayView<'_, Primitive>,
136 indices_array: ArrayView<'_, Primitive>,
137 ctx: &mut ExecutionCtx,
138) -> VortexResult<ArrayRef> {
139 let offsets: &[O] = offsets_array.as_slice();
140 let indices: &[I] = indices_array.as_slice();
141 let data_validity = array
142 .list_validity()
143 .execute_mask(array.as_ref().len(), ctx)?;
144 let indices_validity = indices_array
145 .validity()
146 .vortex_expect("Failed to compute validity mask")
147 .execute_mask(indices_array.as_ref().len(), ctx)?;
148
149 let mut new_offsets = PrimitiveBuilder::<OutputOffsetType>::with_capacity(
150 Nullability::NonNullable,
151 indices.len(),
152 );
153
154 let mut elements_to_take =
162 PrimitiveBuilder::<O>::with_capacity(Nullability::NonNullable, 2 * indices.len());
163
164 let mut current_offset = OutputOffsetType::zero();
165 new_offsets.append_zero();
166
167 for (data_idx, index_valid) in indices.iter().zip(indices_validity.iter()) {
168 if !index_valid {
169 new_offsets.append_value(current_offset);
170 continue;
171 }
172
173 let data_idx: usize = data_idx.as_();
174
175 if !data_validity.value(data_idx) {
176 new_offsets.append_value(current_offset);
177 continue;
178 }
179
180 let start = offsets[data_idx];
181 let stop = offsets[data_idx + 1];
182
183 let additional: usize = (stop - start).as_();
185
186 elements_to_take.reserve_exact(additional);
187 for i in 0..additional {
188 elements_to_take.append_value(start + O::from_usize(i).vortex_expect("i < additional"));
189 }
190 current_offset +=
191 OutputOffsetType::from_usize((stop - start).as_()).vortex_expect("offset conversion");
192 new_offsets.append_value(current_offset);
193 }
194
195 let elements_to_take = elements_to_take.finish();
196 let new_offsets = new_offsets.finish();
197 let new_elements = array.elements().take(elements_to_take)?;
198
199 Ok(ListArray::try_new(
200 new_elements,
201 new_offsets,
202 array.validity()?.take(indices_array.array())?,
203 )?
204 .into_array())
205}
206
207#[cfg(test)]
208mod test {
209 use std::sync::Arc;
210
211 use rstest::rstest;
212 use vortex_buffer::buffer;
213
214 use crate::IntoArray as _;
215 use crate::VortexSessionExecute;
216 use crate::array_session;
217 use crate::arrays::BoolArray;
218 use crate::arrays::ListArray;
219 use crate::arrays::ListViewArray;
220 use crate::arrays::PrimitiveArray;
221 use crate::compute::conformance::take::test_take_conformance;
222 use crate::dtype::DType;
223 use crate::dtype::Nullability;
224 use crate::dtype::PType::I32;
225 use crate::scalar::Scalar;
226 use crate::validity::Validity;
227
228 #[test]
229 fn nullable_take() {
230 let mut ctx = array_session().create_execution_ctx();
231 let list = ListArray::try_new(
232 buffer![0i32, 5, 3, 4].into_array(),
233 buffer![0, 2, 3, 4, 4].into_array(),
234 Validity::Array(BoolArray::from_iter(vec![true, true, false, true]).into_array()),
235 )
236 .unwrap()
237 .into_array();
238
239 let idx =
240 PrimitiveArray::from_option_iter(vec![Some(0), None, Some(1), Some(3)]).into_array();
241
242 let result = list.take(idx).unwrap();
243
244 assert_eq!(
245 result.dtype(),
246 &DType::List(
247 Arc::new(DType::Primitive(I32, Nullability::NonNullable)),
248 Nullability::Nullable
249 )
250 );
251
252 let result = result.execute::<ListViewArray>(&mut ctx).unwrap();
253
254 assert_eq!(result.len(), 4);
255
256 let element_dtype: Arc<DType> = Arc::new(I32.into());
257
258 assert!(
259 result
260 .is_valid(0, &mut array_session().create_execution_ctx())
261 .unwrap()
262 );
263 assert_eq!(
264 result
265 .execute_scalar(0, &mut array_session().create_execution_ctx())
266 .unwrap(),
267 Scalar::list(
268 Arc::clone(&element_dtype),
269 vec![0i32.into(), 5.into()],
270 Nullability::Nullable
271 )
272 );
273
274 assert!(
275 result
276 .is_invalid(1, &mut array_session().create_execution_ctx())
277 .unwrap()
278 );
279
280 assert!(
281 result
282 .is_valid(2, &mut array_session().create_execution_ctx())
283 .unwrap()
284 );
285 assert_eq!(
286 result
287 .execute_scalar(2, &mut array_session().create_execution_ctx())
288 .unwrap(),
289 Scalar::list(
290 Arc::clone(&element_dtype),
291 vec![3i32.into()],
292 Nullability::Nullable
293 )
294 );
295
296 assert!(
297 result
298 .is_valid(3, &mut array_session().create_execution_ctx())
299 .unwrap()
300 );
301 assert_eq!(
302 result
303 .execute_scalar(3, &mut array_session().create_execution_ctx())
304 .unwrap(),
305 Scalar::list(element_dtype, vec![], Nullability::Nullable)
306 );
307 }
308
309 #[test]
310 fn change_validity() {
311 let list = ListArray::try_new(
312 buffer![0i32, 5, 3, 4].into_array(),
313 buffer![0, 2, 3].into_array(),
314 Validity::NonNullable,
315 )
316 .unwrap()
317 .into_array();
318
319 let idx = PrimitiveArray::from_option_iter(vec![Some(0), Some(1), None]).into_array();
320 let result = list.take(idx).unwrap();
323 assert_eq!(
324 result.dtype(),
325 &DType::List(
326 Arc::new(DType::Primitive(I32, Nullability::NonNullable)),
327 Nullability::Nullable
328 )
329 );
330 }
331
332 #[test]
333 fn non_nullable_take() {
334 let mut ctx = array_session().create_execution_ctx();
335 let list = ListArray::try_new(
336 buffer![0i32, 5, 3, 4].into_array(),
337 buffer![0, 2, 3, 3, 4].into_array(),
338 Validity::NonNullable,
339 )
340 .unwrap()
341 .into_array();
342
343 let idx = buffer![1, 0, 2].into_array();
344
345 let result = list.take(idx).unwrap();
346
347 assert_eq!(
348 result.dtype(),
349 &DType::List(
350 Arc::new(DType::Primitive(I32, Nullability::NonNullable)),
351 Nullability::NonNullable
352 )
353 );
354
355 let result = result.execute::<ListViewArray>(&mut ctx).unwrap();
356
357 assert_eq!(result.len(), 3);
358
359 let element_dtype: Arc<DType> = Arc::new(I32.into());
360
361 assert!(
362 result
363 .is_valid(0, &mut array_session().create_execution_ctx())
364 .unwrap()
365 );
366 assert_eq!(
367 result
368 .execute_scalar(0, &mut array_session().create_execution_ctx())
369 .unwrap(),
370 Scalar::list(
371 Arc::clone(&element_dtype),
372 vec![3i32.into()],
373 Nullability::NonNullable
374 )
375 );
376
377 assert!(
378 result
379 .is_valid(1, &mut array_session().create_execution_ctx())
380 .unwrap()
381 );
382 assert_eq!(
383 result
384 .execute_scalar(1, &mut array_session().create_execution_ctx())
385 .unwrap(),
386 Scalar::list(
387 Arc::clone(&element_dtype),
388 vec![0i32.into(), 5.into()],
389 Nullability::NonNullable
390 )
391 );
392
393 assert!(
394 result
395 .is_valid(2, &mut array_session().create_execution_ctx())
396 .unwrap()
397 );
398 assert_eq!(
399 result
400 .execute_scalar(2, &mut array_session().create_execution_ctx())
401 .unwrap(),
402 Scalar::list(element_dtype, vec![], Nullability::NonNullable)
403 );
404 }
405
406 #[test]
407 fn test_take_empty_array() {
408 let list = ListArray::try_new(
409 buffer![0i32, 5, 3, 4].into_array(),
410 buffer![0].into_array(),
411 Validity::NonNullable,
412 )
413 .unwrap()
414 .into_array();
415
416 let idx = PrimitiveArray::empty::<i32>(Nullability::Nullable).into_array();
417
418 let result = list.take(idx).unwrap();
419 assert_eq!(
420 result.dtype(),
421 &DType::List(
422 Arc::new(DType::Primitive(I32, Nullability::NonNullable)),
423 Nullability::Nullable
424 )
425 );
426 assert_eq!(result.len(), 0,);
427 }
428
429 #[rstest]
430 #[case(ListArray::try_new(
431 buffer![0i32, 1, 2, 3, 4, 5].into_array(),
432 buffer![0, 2, 3, 5, 5, 6].into_array(),
433 Validity::NonNullable,
434 ).unwrap())]
435 #[case(ListArray::try_new(
436 buffer![10i32, 20, 30, 40, 50].into_array(),
437 buffer![0, 2, 3, 4, 5].into_array(),
438 Validity::Array(BoolArray::from_iter(vec![true, false, true, true]).into_array()),
439 ).unwrap())]
440 #[case(ListArray::try_new(
441 buffer![1i32, 2, 3].into_array(),
442 buffer![0, 0, 2, 2, 3].into_array(), Validity::NonNullable,
444 ).unwrap())]
445 #[case(ListArray::try_new(
446 buffer![42i32, 43].into_array(),
447 buffer![0, 2].into_array(),
448 Validity::NonNullable,
449 ).unwrap())]
450 #[case({
451 let elements = buffer![0i32..200].into_array();
452 let mut offsets = vec![0u64];
453 for i in 1..=50 {
454 offsets.push(offsets[i - 1] + (i as u64 % 5)); }
456 ListArray::try_new(
457 elements,
458 PrimitiveArray::from_iter(offsets).into_array(),
459 Validity::NonNullable,
460 ).unwrap()
461 })]
462 #[case(ListArray::try_new(
463 PrimitiveArray::from_option_iter([Some(1i32), None, Some(3), Some(4), None]).into_array(),
464 buffer![0, 2, 3, 5].into_array(),
465 Validity::NonNullable,
466 ).unwrap())]
467 fn test_take_list_conformance(#[case] list: ListArray) {
468 test_take_conformance(
469 &list.into_array(),
470 &mut array_session().create_execution_ctx(),
471 );
472 }
473
474 #[test]
475 fn test_u64_offset_accumulation_non_nullable() {
476 let mut ctx = array_session().create_execution_ctx();
477 let elements = buffer![0i32; 200].into_array();
478 let offsets = buffer![0u8, 200].into_array();
479 let list = ListArray::try_new(elements, offsets, Validity::NonNullable)
480 .unwrap()
481 .into_array();
482
483 let idx = buffer![0u8, 0].into_array();
485 let result = list.take(idx).unwrap();
486
487 assert_eq!(result.len(), 2);
488
489 let result_view = result.execute::<ListViewArray>(&mut ctx).unwrap();
490 assert_eq!(result_view.len(), 2);
491 assert!(
492 result_view
493 .is_valid(0, &mut array_session().create_execution_ctx())
494 .unwrap()
495 );
496 assert!(
497 result_view
498 .is_valid(1, &mut array_session().create_execution_ctx())
499 .unwrap()
500 );
501 }
502
503 #[test]
504 fn test_u64_offset_accumulation_nullable() {
505 let mut ctx = array_session().create_execution_ctx();
506 let elements = buffer![0i32; 150].into_array();
507 let offsets = buffer![0u8, 150, 150].into_array();
508 let validity = BoolArray::from_iter(vec![true, false]).into_array();
509 let list = ListArray::try_new(elements, offsets, Validity::Array(validity))
510 .unwrap()
511 .into_array();
512
513 let idx = PrimitiveArray::from_option_iter(vec![Some(0u8), None, Some(0u8)]).into_array();
515 let result = list.take(idx).unwrap();
516
517 assert_eq!(result.len(), 3);
518
519 let result_view = result.execute::<ListViewArray>(&mut ctx).unwrap();
520 assert_eq!(result_view.len(), 3);
521 assert!(
522 result_view
523 .is_valid(0, &mut array_session().create_execution_ctx())
524 .unwrap()
525 );
526 assert!(
527 result_view
528 .is_invalid(1, &mut array_session().create_execution_ctx())
529 .unwrap()
530 );
531 assert!(
532 result_view
533 .is_valid(2, &mut array_session().create_execution_ctx())
534 .unwrap()
535 );
536 }
537
538 #[test]
543 fn test_take_validity_length_mismatch_regression() {
544 let list = ListArray::try_new(
546 buffer![1i32, 2, 3, 4].into_array(),
547 buffer![0, 2, 4].into_array(),
548 Validity::Array(BoolArray::from_iter(vec![true, true]).into_array()),
549 )
550 .unwrap()
551 .into_array();
552
553 let idx = buffer![0u32, 1, 0, 1].into_array();
555
556 let result = list.take(idx).unwrap();
558 assert_eq!(result.len(), 4);
559 }
560}