This is an automated email from the ASF dual-hosted git repository.
scovich pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-rs.git
The following commit(s) were added to refs/heads/main by this push:
new bee4595c13 Add `append_nulls` to `MapBuilder` (#9432)
bee4595c13 is described below
commit bee4595c13665b9dfbd2da3dd0232423a4f2b3c9
Author: Fokko Driesprong <[email protected]>
AuthorDate: Mon Mar 2 22:51:03 2026 +0100
Add `append_nulls` to `MapBuilder` (#9432)
# Which issue does this PR close?
Closes #9431
# Rationale for this change
It would be nice to add `append_nulls` to MapBuilder, similar to
`append_nulls` on `GenericListBuilder`. Appending the nulls at once,
instead of using a loop has some nice performance implications:
```
Benchmark results (1,000,000 nulls):
┌─────────────────────────┬─────────┐
│ Method │ Time │
├─────────────────────────┼─────────┤
│ append(false) in a loop │ 2.36 ms │
├─────────────────────────┼─────────┤
│ append_nulls(N) │ 50 µs │
└─────────────────────────┴─────────┘
```
# What changes are included in this PR?
A new public API.
# Are these changes tested?
With some fresh unit tests.
# Are there any user-facing changes?
A nice and convient new public API
---
arrow-array/src/builder/map_builder.rs | 63 +++++++++++++++++++++++++++++++---
1 file changed, 59 insertions(+), 4 deletions(-)
diff --git a/arrow-array/src/builder/map_builder.rs
b/arrow-array/src/builder/map_builder.rs
index b70d4b7388..5ff1625b49 100644
--- a/arrow-array/src/builder/map_builder.rs
+++ b/arrow-array/src/builder/map_builder.rs
@@ -154,11 +154,9 @@ impl<K: ArrayBuilder, V: ArrayBuilder> MapBuilder<K, V> {
(&mut self.key_builder, &mut self.value_builder)
}
- /// Finish the current map array slot
- ///
- /// Returns an error if the key and values builders are in an inconsistent
state.
+ /// Validates that key and value builders have equal lengths.
#[inline]
- pub fn append(&mut self, is_valid: bool) -> Result<(), ArrowError> {
+ fn validate_equal_lengths(&self) -> Result<(), ArrowError> {
if self.key_builder.len() != self.value_builder.len() {
return Err(ArrowError::InvalidArgumentError(format!(
"Cannot append to a map builder when its keys and values have
unequal lengths of {} and {}",
@@ -166,11 +164,32 @@ impl<K: ArrayBuilder, V: ArrayBuilder> MapBuilder<K, V> {
self.value_builder.len()
)));
}
+ Ok(())
+ }
+
+ /// Finish the current map array slot
+ ///
+ /// Returns an error if the key and values builders are in an inconsistent
state.
+ #[inline]
+ pub fn append(&mut self, is_valid: bool) -> Result<(), ArrowError> {
+ self.validate_equal_lengths()?;
self.offsets_builder.push(self.key_builder.len() as i32);
self.null_buffer_builder.append(is_valid);
Ok(())
}
+ /// Append `n` nulls to this [`MapBuilder`]
+ ///
+ /// Returns an error if the key and values builders are in an inconsistent
state.
+ #[inline]
+ pub fn append_nulls(&mut self, n: usize) -> Result<(), ArrowError> {
+ self.validate_equal_lengths()?;
+ let offset = self.key_builder.len() as i32;
+ self.offsets_builder.extend(std::iter::repeat_n(offset, n));
+ self.null_buffer_builder.append_n_nulls(n);
+ Ok(())
+ }
+
/// Builds the [`MapArray`]
pub fn finish(&mut self) -> MapArray {
let len = self.len();
@@ -436,6 +455,42 @@ mod tests {
);
}
+ #[test]
+ fn test_append_nulls() {
+ let mut builder = MapBuilder::new(None, Int32Builder::new(),
Int32Builder::new());
+
+ builder.keys().append_value(1);
+ builder.values().append_value(100);
+ builder.append(true).unwrap();
+
+ builder.append_nulls(3).unwrap();
+
+ builder.keys().append_value(2);
+ builder.values().append_value(200);
+ builder.append(true).unwrap();
+
+ let map = builder.finish();
+ assert_eq!(map.len(), 5);
+ assert_eq!(map.null_count(), 3);
+ assert!(map.is_valid(0));
+ assert!(map.is_null(1));
+ assert!(map.is_null(2));
+ assert!(map.is_null(3));
+ assert!(map.is_valid(4));
+ assert_eq!(map.value_offsets(), &[0, 1, 1, 1, 1, 2]);
+ }
+
+ #[test]
+ fn test_append_nulls_inconsistent_state() {
+ let mut builder = MapBuilder::new(None, Int32Builder::new(),
Int32Builder::new());
+ // Add a key without a matching value
+ builder.keys().append_value(1);
+
+ let result = builder.append_nulls(2);
+ assert!(result.is_err());
+ assert!(result.unwrap_err().to_string().contains("unequal lengths"));
+ }
+
#[test]
#[should_panic(expected = "Keys field must not be nullable")]
fn test_with_nullable_keys_field() {