Skip to content

Commit 3cdd28b

Browse files
Make VariantArray iterable
1 parent 77ca6dc commit 3cdd28b

File tree

1 file changed

+179
-0
lines changed

1 file changed

+179
-0
lines changed

parquet-variant-compute/src/variant_array.rs

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,11 @@ impl VariantArray {
422422
pub fn is_valid(&self, index: usize) -> bool {
423423
!self.is_null(index)
424424
}
425+
426+
/// Returns an iterator over the values in this array
427+
pub fn iter(&self) -> VariantArrayIter<'_> {
428+
VariantArrayIter::new(self)
429+
}
425430
}
426431

427432
impl From<VariantArray> for StructArray {
@@ -436,6 +441,101 @@ impl From<VariantArray> for ArrayRef {
436441
}
437442
}
438443

444+
/// An iterator over [`VariantArray`]
445+
///
446+
/// This iterator returns `Option<Option<Variant<'a, 'a>>>` where:
447+
/// - `None` indicates the end of iteration
448+
/// - `Some(None)` indicates a null value at this position
449+
/// - `Some(Some(variant))` indicates a valid variant value
450+
///
451+
/// This iterator follows similar conventions to [`ArrayIter`] from arrow-array.
452+
///
453+
/// # Example
454+
///
455+
/// ```
456+
/// # use parquet_variant::Variant;
457+
/// # use parquet_variant_compute::VariantArrayBuilder;
458+
/// let mut builder = VariantArrayBuilder::new(10);
459+
/// builder.append_variant(Variant::from(42));
460+
/// builder.append_null();
461+
/// builder.append_variant(Variant::from("hello"));
462+
/// let array = builder.build();
463+
///
464+
/// let values = array.iter().collect::<Vec<_>>();
465+
/// assert_eq!(values.len(), 3);
466+
/// assert_eq!(values[0], Some(Variant::from(42)));
467+
/// assert_eq!(values[1], None);
468+
/// assert_eq!(values[2], Some(Variant::from("hello")));
469+
/// ```
470+
#[derive(Debug)]
471+
pub struct VariantArrayIter<'a> {
472+
array: &'a VariantArray,
473+
head_i: usize,
474+
tail_i: usize,
475+
}
476+
477+
impl<'a> VariantArrayIter<'a> {
478+
/// Creates a new iterator over the given [`VariantArray`]
479+
pub fn new(array: &'a VariantArray) -> Self {
480+
Self {
481+
array,
482+
head_i: 0,
483+
tail_i: array.len(),
484+
}
485+
}
486+
487+
/// Helper method to check if the value at the given index is null
488+
#[inline]
489+
fn is_null(&self, idx: usize) -> bool {
490+
self.array.is_null(idx)
491+
}
492+
}
493+
494+
impl<'a> Iterator for VariantArrayIter<'a> {
495+
type Item = Option<Variant<'a, 'a>>;
496+
497+
#[inline]
498+
fn next(&mut self) -> Option<Self::Item> {
499+
if self.head_i == self.tail_i {
500+
return None;
501+
}
502+
503+
let out = if self.is_null(self.head_i) {
504+
Some(None)
505+
} else {
506+
Some(Some(self.array.value(self.head_i)))
507+
};
508+
509+
self.head_i += 1;
510+
511+
return out;
512+
}
513+
514+
fn size_hint(&self) -> (usize, Option<usize>) {
515+
let remainder = self.tail_i - self.head_i;
516+
517+
(remainder, Some(remainder))
518+
}
519+
}
520+
521+
impl<'a> DoubleEndedIterator for VariantArrayIter<'a> {
522+
fn next_back(&mut self) -> Option<Self::Item> {
523+
if self.head_i == self.tail_i {
524+
return None;
525+
}
526+
527+
self.tail_i -= 1;
528+
529+
if self.is_null(self.tail_i) {
530+
Some(None)
531+
} else {
532+
Some(Some(self.array.value(self.tail_i)))
533+
}
534+
}
535+
}
536+
537+
impl<'a> ExactSizeIterator for VariantArrayIter<'a> {}
538+
439539
/// One shredded field of a partially or prefectly shredded variant. For example, suppose the
440540
/// shredding schema for variant `v` treats it as an object with a single field `a`, where `a` is
441541
/// itself a struct with the single field `b` of type INT. Then the physical layout of the column
@@ -1062,6 +1162,8 @@ fn canonicalize_and_verify_field(field: &Arc<Field>) -> Result<Cow<'_, Arc<Field
10621162

10631163
#[cfg(test)]
10641164
mod test {
1165+
use crate::VariantArrayBuilder;
1166+
10651167
use super::*;
10661168
use arrow::array::{BinaryViewArray, Int32Array};
10671169
use arrow_schema::{Field, Fields};
@@ -1244,4 +1346,81 @@ mod test {
12441346
}
12451347
));
12461348
}
1349+
1350+
#[test]
1351+
fn test_variant_array_iterable() {
1352+
let mut b = VariantArrayBuilder::new(6);
1353+
1354+
b.append_null();
1355+
b.append_variant(Variant::from(1_i8));
1356+
b.append_variant(Variant::Null);
1357+
b.append_variant(Variant::from(2_i32));
1358+
b.append_variant(Variant::from(3_i64));
1359+
b.append_null();
1360+
1361+
let v = b.build();
1362+
1363+
let variants = v.iter().collect::<Vec<_>>();
1364+
1365+
assert_eq!(
1366+
variants,
1367+
vec![
1368+
None,
1369+
Some(Variant::Int8(1)),
1370+
Some(Variant::Null),
1371+
Some(Variant::Int32(2)),
1372+
Some(Variant::Int64(3)),
1373+
None,
1374+
]
1375+
);
1376+
}
1377+
1378+
#[test]
1379+
fn test_variant_array_iter_double_ended() {
1380+
let mut b = VariantArrayBuilder::new(5);
1381+
1382+
b.append_variant(Variant::from(0_i32));
1383+
b.append_null();
1384+
b.append_variant(Variant::from(2_i32));
1385+
b.append_null();
1386+
b.append_variant(Variant::from(4_i32));
1387+
1388+
let array = b.build();
1389+
let mut iter = array.iter();
1390+
1391+
assert_eq!(iter.next(), Some(Some(Variant::from(0_i32))));
1392+
assert_eq!(iter.next(), Some(None));
1393+
1394+
assert_eq!(iter.next_back(), Some(Some(Variant::from(4_i32))));
1395+
assert_eq!(iter.next_back(), Some(None));
1396+
assert_eq!(iter.next_back(), Some(Some(Variant::from(2_i32))));
1397+
1398+
assert_eq!(iter.next_back(), None);
1399+
assert_eq!(iter.next(), None);
1400+
}
1401+
1402+
#[test]
1403+
fn test_variant_array_iter_reverse() {
1404+
let mut b = VariantArrayBuilder::new(5);
1405+
1406+
b.append_variant(Variant::from("a"));
1407+
b.append_null();
1408+
b.append_variant(Variant::from("aaa"));
1409+
b.append_null();
1410+
b.append_variant(Variant::from("aaaaa"));
1411+
1412+
let array = b.build();
1413+
1414+
let result: Vec<_> = array.iter().rev().collect();
1415+
assert_eq!(
1416+
result,
1417+
vec![
1418+
Some(Variant::from("aaaaa")),
1419+
None,
1420+
Some(Variant::from("aaa")),
1421+
None,
1422+
Some(Variant::from("a")),
1423+
]
1424+
);
1425+
}
12471426
}

0 commit comments

Comments
 (0)