diff --git a/ruby/red-arrow-format/lib/arrow-format/array.rb b/ruby/red-arrow-format/lib/arrow-format/array.rb index 20929493e90..ebf8b1ec684 100644 --- a/ruby/red-arrow-format/lib/arrow-format/array.rb +++ b/ruby/red-arrow-format/lib/arrow-format/array.rb @@ -1037,6 +1037,53 @@ def slice!(offset, size) end class ListArray < VariableSizeListArray + include BufferAlignable + + def initialize(type, *args) + if args.size == 1 + args = build_data(type, args.first) + elsif args.size != 4 + raise ArgumentError, + "wrong number of arguments (given #{args.size + 1}, expected 2 or 5)" + end + + super(type, *args) + end + + private + def build_data(type, data) + n = 0 + validity_buffer_builder = nil + + child_values = [] + offsets = [0] + data.each_with_index do |value, i| + if value.nil? + validity_buffer_builder ||= SparseBitmapBuilder.new + validity_buffer_builder.unset(i) + else + child_values.concat(value) + end + offsets << child_values.size + n += 1 + end + + validity_buffer = validity_buffer_builder&.finish(n) + + offsets_data = offsets.pack("#{type.offset_pack_template}*") + pad!(offsets_data, buffer_padding_size(offsets_data)) + offsets_data.freeze + offsets_buffer = IO::Buffer.for(offsets_data) + + child = type.child.type.build_array(child_values) + + [ + n, + validity_buffer, + offsets_buffer, + child, + ] + end end class LargeListArray < VariableSizeListArray diff --git a/ruby/red-arrow-format/lib/arrow-format/type.rb b/ruby/red-arrow-format/lib/arrow-format/type.rb index 9a6d1f3ed06..432dfdf3f81 100644 --- a/ruby/red-arrow-format/lib/arrow-format/type.rb +++ b/ruby/red-arrow-format/lib/arrow-format/type.rb @@ -1021,6 +1021,10 @@ def offset_buffer_type :s32 # TODO: big endian support end + def offset_pack_template + "l" + end + def build_array(...) ListArray.new(self, ...) end diff --git a/ruby/red-arrow-format/test/test-list-array.rb b/ruby/red-arrow-format/test/test-list-array.rb new file mode 100644 index 00000000000..d3494a09c60 --- /dev/null +++ b/ruby/red-arrow-format/test/test-list-array.rb @@ -0,0 +1,54 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +class TestListArray < Test::Unit::TestCase + def setup + child = ArrowFormat::Field.new("item", ArrowFormat::Int32Type.singleton) + @type = ArrowFormat::ListType.new(child) + end + + sub_test_case("#initialize") do + def test_no_null + values = [[-1, 0], [], [1, 2, 3]] + array = ArrowFormat::ListArray.new(@type, values) + assert_same(@type, array.type) + assert_equal(values, array.to_a) + end + + def test_null_list + values = [[1, 2], nil, [], [3]] + array = ArrowFormat::ListArray.new(@type, values) + assert_equal([0, 2, 2, 2, 3], array.offsets) + assert_equal([1, 2, 3], array.child.to_a) + assert_equal(values, array.to_a) + end + + def test_null_child + values = [[1, nil], [], [nil, 2]] + array = ArrowFormat::ListArray.new(@type, values) + assert_equal([1, nil, nil, 2], array.child.to_a) + assert_equal(values, array.to_a) + end + + def test_empty + array = ArrowFormat::ListArray.new(@type, []) + assert_equal([0], array.offsets) + assert_equal([], array.child.to_a) + assert_equal([], array.to_a) + end + end +end