diff --git a/cpp/src/arrow/array/array_list_test.cc b/cpp/src/arrow/array/array_list_test.cc index 063b68706b31..3d18d5f967b7 100644 --- a/cpp/src/arrow/array/array_list_test.cc +++ b/cpp/src/arrow/array/array_list_test.cc @@ -1369,14 +1369,26 @@ TEST_F(TestMapArray, FromArrays) { ASSERT_RAISES(Invalid, MapArray::FromArrays(offsets1, keys_with_null, tmp_items, pool_)); - // With null_bitmap - ASSERT_OK_AND_ASSIGN(auto map7, MapArray::FromArrays(offsets1, keys, items, pool_, - offsets3->data()->buffers[0])); + // With null_bitmap and null_count=1 + auto null_bitmap_1 = ArrayFromJSON(boolean(), "[1, 0, 1]")->data()->buffers[1]; + ASSERT_OK_AND_ASSIGN(auto map7, + MapArray::FromArrays(offsets1, keys, items, pool_, null_bitmap_1)); ASSERT_OK(map7->Validate()); MapArray expected7(map_type, length, offsets1->data()->buffers[1], keys, items, - offsets3->data()->buffers[0], 1); + null_bitmap_1, 1); + ASSERT_EQ(map7->null_count(), 1); AssertArraysEqual(expected7, *map7); + // With null_bitmap and null_count=2 + auto null_bitmap_2 = ArrayFromJSON(boolean(), "[0, 1, 0]")->data()->buffers[1]; + ASSERT_OK_AND_ASSIGN(auto map8, + MapArray::FromArrays(offsets1, keys, items, pool_, null_bitmap_2)); + ASSERT_OK(map8->Validate()); + MapArray expected8(map_type, length, offsets1->data()->buffers[1], keys, items, + null_bitmap_2, 2); + ASSERT_EQ(map8->null_count(), 2); + AssertArraysEqual(expected8, *map8); + // Null bitmap and offset with null ASSERT_RAISES(Invalid, MapArray::FromArrays(offsets3, keys, items, pool_, offsets3->data()->buffers[0])); diff --git a/cpp/src/arrow/array/array_nested.cc b/cpp/src/arrow/array/array_nested.cc index 2f6bca3d571e..47c0fd35829a 100644 --- a/cpp/src/arrow/array/array_nested.cc +++ b/cpp/src/arrow/array/array_nested.cc @@ -807,7 +807,7 @@ MapArray::MapArray(const std::shared_ptr& type, int64_t length, Result> MapArray::FromArraysInternal( std::shared_ptr type, const std::shared_ptr& offsets, const std::shared_ptr& keys, const std::shared_ptr& items, - MemoryPool* pool, const std::shared_ptr& null_bitmap) { + MemoryPool* pool, std::shared_ptr null_bitmap) { using offset_type = typename MapType::offset_type; using OffsetArrowType = typename CTypeTraits::ArrowType; @@ -836,7 +836,7 @@ Result> MapArray::FromArraysInternal( return Status::NotImplemented("Null bitmap with offsets slice not supported."); } - if (offsets->null_count() > 0) { + if (offsets->data()->MayHaveNulls()) { ARROW_ASSIGN_OR_RAISE(auto buffers, CleanListOffsets(NULLPTR, *offsets, pool)); return std::make_shared(type, offsets->length() - 1, std::move(buffers), @@ -847,30 +847,32 @@ Result> MapArray::FromArraysInternal( const auto& typed_offsets = checked_cast(*offsets); BufferVector buffers; - int64_t null_count; - if (null_bitmap != nullptr) { - buffers = BufferVector({std::move(null_bitmap), typed_offsets.values()}); - null_count = null_bitmap->size(); - } else { - buffers = BufferVector({null_bitmap, typed_offsets.values()}); - null_count = 0; + buffers.resize(2); + int64_t null_count = 0; + if (null_bitmap) { + buffers[0] = std::move(null_bitmap); + null_count = kUnknownNullCount; } + buffers[1] = typed_offsets.values(); return std::make_shared(type, offsets->length() - 1, std::move(buffers), keys, items, /*null_count=*/null_count, offsets->offset()); } -Result> MapArray::FromArrays( - const std::shared_ptr& offsets, const std::shared_ptr& keys, - const std::shared_ptr& items, MemoryPool* pool, - const std::shared_ptr& null_bitmap) { +Result> MapArray::FromArrays(const std::shared_ptr& offsets, + const std::shared_ptr& keys, + const std::shared_ptr& items, + MemoryPool* pool, + std::shared_ptr null_bitmap) { return FromArraysInternal(std::make_shared(keys->type(), items->type()), - offsets, keys, items, pool, null_bitmap); + offsets, keys, items, pool, std::move(null_bitmap)); } -Result> MapArray::FromArrays( - std::shared_ptr type, const std::shared_ptr& offsets, - const std::shared_ptr& keys, const std::shared_ptr& items, - MemoryPool* pool, const std::shared_ptr& null_bitmap) { +Result> MapArray::FromArrays(std::shared_ptr type, + const std::shared_ptr& offsets, + const std::shared_ptr& keys, + const std::shared_ptr& items, + MemoryPool* pool, + std::shared_ptr null_bitmap) { if (type->id() != Type::MAP) { return Status::TypeError("Expected map type, got ", type->ToString()); } @@ -881,7 +883,8 @@ Result> MapArray::FromArrays( if (!map_type.item_type()->Equals(items->type())) { return Status::TypeError("Mismatching map items type"); } - return FromArraysInternal(std::move(type), offsets, keys, items, pool, null_bitmap); + return FromArraysInternal(std::move(type), offsets, keys, items, pool, + std::move(null_bitmap)); } Status MapArray::ValidateChildData( diff --git a/cpp/src/arrow/array/array_nested.h b/cpp/src/arrow/array/array_nested.h index f96b6bd3b134..a6d4977839ef 100644 --- a/cpp/src/arrow/array/array_nested.h +++ b/cpp/src/arrow/array/array_nested.h @@ -537,13 +537,13 @@ class ARROW_EXPORT MapArray : public ListArray { static Result> FromArrays( const std::shared_ptr& offsets, const std::shared_ptr& keys, const std::shared_ptr& items, MemoryPool* pool = default_memory_pool(), - const std::shared_ptr& null_bitmap = NULLPTR); + std::shared_ptr null_bitmap = NULLPTR); static Result> FromArrays( std::shared_ptr type, const std::shared_ptr& offsets, const std::shared_ptr& keys, const std::shared_ptr& items, MemoryPool* pool = default_memory_pool(), - const std::shared_ptr& null_bitmap = NULLPTR); + std::shared_ptr null_bitmap = NULLPTR); const MapType* map_type() const { return map_type_; } @@ -563,7 +563,7 @@ class ARROW_EXPORT MapArray : public ListArray { static Result> FromArraysInternal( std::shared_ptr type, const std::shared_ptr& offsets, const std::shared_ptr& keys, const std::shared_ptr& items, - MemoryPool* pool, const std::shared_ptr& null_bitmap = NULLPTR); + MemoryPool* pool, std::shared_ptr null_bitmap = NULLPTR); private: const MapType* map_type_; diff --git a/python/pyarrow/tests/test_array.py b/python/pyarrow/tests/test_array.py index 88394c77e429..b358403f5ee3 100644 --- a/python/pyarrow/tests/test_array.py +++ b/python/pyarrow/tests/test_array.py @@ -1097,6 +1097,7 @@ def test_map_from_arrays(): items.type), mask=pa.array([False, True, False], type=pa.bool_()) ) + assert result.null_count == 1 assert result.equals(expected) # pass in null bitmap without the type @@ -1106,6 +1107,19 @@ def test_map_from_arrays(): ) assert result.equals(expected) + # pass in null bitmap with two nulls + offsets = [0, None, None, 6] + pyentries = [None, None, pypairs[2:]] + + result = pa.MapArray.from_arrays([0, 2, 2, 6], keys, items, pa.map_( + keys.type, + items.type), + mask=pa.array([True, True, False], type=pa.bool_()) + ) + expected = pa.array(pyentries, type=pa.map_(pa.binary(), pa.int32())) + assert result.null_count == 2 + assert result.equals(expected) + # error if null bitmap and offsets with nulls passed msg1 = 'Ambiguous to specify both validity map and offsets with nulls' with pytest.raises(pa.ArrowInvalid, match=msg1):