This is an automated email from the ASF dual-hosted git repository.
kevingurney pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow.git
The following commit(s) were added to refs/heads/main by this push:
new e14f60b7ef GH-38398: [MATLAB] Improve array display (#38400)
e14f60b7ef is described below
commit e14f60b7efd2d41c6ede8569ac1a8b07da3cbf73
Author: sgilmore10 <[email protected]>
AuthorDate: Tue Oct 24 13:42:41 2023 -0400
GH-38398: [MATLAB] Improve array display (#38400)
### Rationale for this change
Currently, the display for `arrow.array.Array`s is not very MATLAB-like:
```matlab
>> a = arrow.array([1 2 3 4])
a =
[
1,
2,
3,
]
```
At the very least, the display should include the class header and indent
each line by 4 spaces. Here's one display option:
```matlab
>> a = arrow.array([1 2 3 4])
a =
Float64Array with 4 elements and 0 null values:
1 | 2 | 3 | 4
```
### What changes are included in this PR?
1. Array display now includes a class header, which states the array type
and the number of elements/nulls in the array.
2. Changed the
[`window`](https://github.com/apache/arrow/blob/37935604bf168a3b2d52f3cc5b0edf83b5783309/cpp/src/arrow/pretty_print.h#L79C1-L80C1)
size to 3 from 10.
3. Primitive and string arrays are displayed horizontally, i.e. set
[`skip_new_lines`](https://github.com/apache/arrow/blob/37935604bf168a3b2d52f3cc5b0edf83b5783309/cpp/src/arrow/pretty_print.h#L90)
to `false`. Uses ` | ` as the delimiter between elements with no
opening/closing brackets.
4. All other array types (`struct`, `list`, etc) are displayed vertically
with an `indent` of 4.
**Example String Array Display:**
```matlab
>> a = arrow.array(["Hello", missing, "Bye"])
a =
StringArray with 3 elements and 1 null value:
"Hello" | null | "Bye"
```
**Example Struct Array Display:**
```matlab
>> a1 = arrow.array(["Hello", missing, "Bye"]);
>> a2 = arrow.array([1 2 3]);
>> structArray = arrow.array.StructArray.fromArrays(a1, a2)
structArray =
StructArray with 3 elements and 0 null values:
-- is_valid: all not null
-- child 0 type: string
[
"Hello",
null,
"Bye"
]
-- child 1 type: double
[
1,
2,
3
]
```
### Are these changes tested?
Yes. Added a new test class called `tArrayDisplay.m` with unit tests for
array display.
### Are there any user-facing changes?
Yes. Users will see a different display for arrays now.
### Future Directions
1. #38166
* Closes: #38398
Lead-authored-by: Sarah Gilmore <[email protected]>
Co-authored-by: sgilmore10 <[email protected]>
Co-authored-by: Sutou Kouhei <[email protected]>
Signed-off-by: Kevin Gurney <[email protected]>
---
matlab/src/cpp/arrow/matlab/array/proxy/array.cc | 30 +-
matlab/src/cpp/arrow/matlab/error/error.h | 1 +
.../+arrow/+array/+internal/+display/getHeader.m | 38 +++
.../+internal/+display/pluralizeStringIfNeeded.m | 23 ++
matlab/src/matlab/+arrow/+array/Array.m | 16 +-
matlab/test/arrow/array/tArrayDisplay.m | 334 +++++++++++++++++++++
6 files changed, 439 insertions(+), 3 deletions(-)
diff --git a/matlab/src/cpp/arrow/matlab/array/proxy/array.cc
b/matlab/src/cpp/arrow/matlab/array/proxy/array.cc
index bd8e9dc842..4e52c990d3 100644
--- a/matlab/src/cpp/arrow/matlab/array/proxy/array.cc
+++ b/matlab/src/cpp/arrow/matlab/array/proxy/array.cc
@@ -21,10 +21,13 @@
#include "arrow/matlab/bit/unpack.h"
#include "arrow/matlab/error/error.h"
#include "arrow/matlab/type/proxy/wrap.h"
+#include "arrow/pretty_print.h"
#include "arrow/type_traits.h"
#include "libmexclass/proxy/ProxyManager.h"
+#include <sstream>
+
namespace arrow::matlab::array::proxy {
Array::Array(std::shared_ptr<arrow::Array> array) :
array{std::move(array)} {
@@ -44,7 +47,32 @@ namespace arrow::matlab::array::proxy {
void Array::toString(libmexclass::proxy::method::Context& context) {
::matlab::data::ArrayFactory factory;
- const auto str_utf8 = array->ToString();
+
+ auto opts = arrow::PrettyPrintOptions::Defaults();
+ opts.window = 3;
+ opts.indent = 4;
+ opts.indent_size = 4;
+
+ const auto type_id = array->type()->id();
+ if (arrow::is_primitive(type_id) || arrow::is_string(type_id)) {
+ /*
+ * Display primitive and string types horizontally without
+ * opening and closing delimiters. Use " | " as the delimiter
+ * between elments. Below is an example Int32Array display:
+ *
+ * 1 | 2 | 3 | ... | 6 | 7 | 8
+ */
+ opts.skip_new_lines = true;
+ opts.array_delimiters.open = "";
+ opts.array_delimiters.close = "";
+ opts.array_delimiters.element = " | ";
+ }
+
+ std::stringstream ss;
+ MATLAB_ERROR_IF_NOT_OK_WITH_CONTEXT(arrow::PrettyPrint(*array, opts,
&ss), context, error::ARRAY_PRETTY_PRINT_FAILED);
+
+ const auto str_utf8 = opts.skip_new_lines ? " " + ss.str() :
ss.str();
+
MATLAB_ASSIGN_OR_ERROR_WITH_CONTEXT(const auto str_utf16,
arrow::util::UTF8StringToUTF16(str_utf8), context,
error::UNICODE_CONVERSION_ERROR_ID);
auto str_mda = factory.createScalar(str_utf16);
context.outputs[0] = str_mda;
diff --git a/matlab/src/cpp/arrow/matlab/error/error.h
b/matlab/src/cpp/arrow/matlab/error/error.h
index a512fdcc89..2d8f5c432c 100644
--- a/matlab/src/cpp/arrow/matlab/error/error.h
+++ b/matlab/src/cpp/arrow/matlab/error/error.h
@@ -201,4 +201,5 @@ namespace arrow::matlab::error {
static const char* INDEX_EMPTY_CONTAINER = "arrow:index:EmptyContainer";
static const char* INDEX_OUT_OF_RANGE = "arrow:index:OutOfRange";
static const char* BUFFER_VIEW_OR_COPY_FAILED =
"arrow:buffer:ViewOrCopyFailed";
+ static const char* ARRAY_PRETTY_PRINT_FAILED =
"arrow:array:PrettyPrintFailed";
}
diff --git a/matlab/src/matlab/+arrow/+array/+internal/+display/getHeader.m
b/matlab/src/matlab/+arrow/+array/+internal/+display/getHeader.m
new file mode 100644
index 0000000000..5c8704d5bf
--- /dev/null
+++ b/matlab/src/matlab/+arrow/+array/+internal/+display/getHeader.m
@@ -0,0 +1,38 @@
+%GETHEADER Generates the display header for arrow.array.Array classes
+
+% 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.
+
+function header = getHeader(className, numElements, numNulls)
+ import arrow.array.internal.display.pluralizeStringIfNeeded
+ elementString = pluralizeStringIfNeeded(numElements, "element");
+
+ nullString = pluralizeStringIfNeeded(numNulls, "null value");
+
+ numString = "%d";
+ if usejava("desktop")
+ % Bold the number of elements and nulls if the desktop is enabled
+ numString = compose("<strong>%s</strong>", numString);
+ end
+
+ formatSpec = " %s with " + numString + " %s and " + numString + " %s";
+ if numElements > 0
+ formatSpec = formatSpec + ":";
+ end
+ formatSpec = formatSpec + newline;
+
+ header = compose(formatSpec, className, numElements, elementString,
numNulls, nullString);
+ header = char(header);
+end
\ No newline at end of file
diff --git
a/matlab/src/matlab/+arrow/+array/+internal/+display/pluralizeStringIfNeeded.m
b/matlab/src/matlab/+arrow/+array/+internal/+display/pluralizeStringIfNeeded.m
new file mode 100644
index 0000000000..ae9eafba92
--- /dev/null
+++
b/matlab/src/matlab/+arrow/+array/+internal/+display/pluralizeStringIfNeeded.m
@@ -0,0 +1,23 @@
+%PLURALIZESTRINGIFNEEDED Pluralizes str if num is not equal to 1.
+
+% 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.
+
+function str = pluralizeStringIfNeeded(num, str)
+ if num ~= 1
+ str = str + "s";
+ end
+end
+
diff --git a/matlab/src/matlab/+arrow/+array/Array.m
b/matlab/src/matlab/+arrow/+array/Array.m
index 381e734983..293ad87ad4 100644
--- a/matlab/src/matlab/+arrow/+array/Array.m
+++ b/matlab/src/matlab/+arrow/+array/Array.m
@@ -62,8 +62,21 @@ classdef (Abstract) Array < matlab.mixin.CustomDisplay & ...
end
methods (Access=protected)
+ function header = getHeader(obj)
+ name = matlab.mixin.CustomDisplay.getClassNameForHeader(obj);
+ numElements = obj.NumElements;
+ % TODO: Add NumValid and NumNull as properties to Array to
+ % avoid materializing the Valid property. This will improve
+ % performance for large arrays.
+ numNulls = nnz(~obj.Valid);
+ header = arrow.array.internal.display.getHeader(name, numElements,
numNulls);
+ end
+
function displayScalarObject(obj)
- disp(obj.toString());
+ disp(getHeader(obj));
+ if obj.NumElements > 0
+ disp(toString(obj) + newline);
+ end
end
end
@@ -86,4 +99,3 @@ classdef (Abstract) Array < matlab.mixin.CustomDisplay & ...
end
end
end
-
diff --git a/matlab/test/arrow/array/tArrayDisplay.m
b/matlab/test/arrow/array/tArrayDisplay.m
new file mode 100644
index 0000000000..578f5fe82a
--- /dev/null
+++ b/matlab/test/arrow/array/tArrayDisplay.m
@@ -0,0 +1,334 @@
+%TARRAYDISPLAY Unit tests for array display.
+
+classdef tArrayDisplay < matlab.unittest.TestCase
+% 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.
+
+ properties (TestParameter)
+ EmptyArray
+ ArrayWithOneElement
+ ArrayWithMultipleElements
+ ArrayWithOneNull
+ ArrayWithMultipleNulls
+ end
+
+ methods(TestParameterDefinition, Static)
+ function EmptyArray = initializeEmptyArray()
+ EmptyArray.Boolean = arrow.array(logical.empty(0, 0));
+ EmptyArray.UInt8 = arrow.array(uint8.empty(0, 0));
+ EmptyArray.Int64 = arrow.array(int64.empty(0, 0));
+ EmptyArray.Time64 = arrow.array(duration.empty(0, 0));
+ EmptyArray.Timestamp = arrow.array(datetime.empty(0, 0));
+ EmptyArray.String = arrow.array(string.empty(0, 0));
+ end
+
+ function ArrayWithOneElement = initializeArrayWithOneElement()
+ boolStruct = struct(Array=arrow.array(true), String="true");
+ ArrayWithOneElement.Boolean = boolStruct;
+
+ uint8Struct = struct(Array=arrow.array(uint8(1)), String="1");
+ ArrayWithOneElement.UInt8 = uint8Struct;
+
+ int64Struct = struct(Array=arrow.array(int64(2)), String="2");
+ ArrayWithOneElement.Int64 = int64Struct;
+
+ time64Struct = struct(Array=arrow.array(seconds(1)),
String="00:00:01.000000");
+ ArrayWithOneElement.Time64 = time64Struct;
+
+ timestampStruct = struct(Array=arrow.array(datetime(2023, 10, 20),
TimeUnit="Second"), String="2023-10-20 00:00:00");
+ ArrayWithOneElement.Timestamp = timestampStruct;
+
+ stringStruct = struct(Array=arrow.array("A"), String="""A""");
+ ArrayWithOneElement.String = stringStruct;
+ end
+
+ function ArrayWithMultipleElements =
initializeArrayWithMultipleElements()
+ boolStruct = struct(Array=arrow.array([true false true]), ...
+ String="true | false | true");
+ ArrayWithMultipleElements.Boolean = boolStruct;
+
+ uint8Struct = struct(Array=arrow.array(uint8([1 10 100])), ...
+ String="1 | 10 | 100");
+ ArrayWithMultipleElements.UInt8 = uint8Struct;
+
+ int64Struct = struct(Array=arrow.array(int64([2 1 20])), ...
+ String="2 | 1 | 20");
+ ArrayWithMultipleElements.Int64 = int64Struct;
+
+ time64Struct = struct(Array=arrow.array(seconds([1 2 3])), ...
+ String="00:00:01.000000 | 00:00:02.000000 | 00:00:03.000000");
+ ArrayWithMultipleElements.Time64 = time64Struct;
+
+ timestampStruct = struct(Array=arrow.array(datetime(2023, 10, 20)
+ days(0:2) , TimeUnit="Second"), ...
+ String="2023-10-20 00:00:00 | 2023-10-21 00:00:00 | 2023-10-22
00:00:00");
+ ArrayWithMultipleElements.Timestamp = timestampStruct;
+
+ stringStruct = struct(Array=arrow.array(["A" "ABC" "ABCDE"]), ...
+ String="""A"" | ""ABC"" | ""ABCDE""");
+ ArrayWithMultipleElements.String = stringStruct;
+ end
+
+ function ArrayWithOneNull = initializeArrayWithOneNull()
+ boolStruct = struct(Array=arrow.array([true false true], Valid=[1
3]), ...
+ String="true | null | true");
+ ArrayWithOneNull.Boolean = boolStruct;
+
+ uint8Struct = struct(Array=arrow.array(uint8([1 10 100]), Valid=[2
3]), ...
+ String="null | 10 | 100");
+ ArrayWithOneNull.UInt8 = uint8Struct;
+
+ int64Struct = struct(Array=arrow.array(int64([2 1 20]), Valid=[1
2]), ...
+ String="2 | 1 | null");
+ ArrayWithOneNull.Int64 = int64Struct;
+
+ time64Struct = struct(Array=arrow.array(seconds([1 NaN 3])), ...
+ String="00:00:01.000000 | null | 00:00:03.000000");
+ ArrayWithOneNull.Time64 = time64Struct;
+
+ timestampStruct = struct(Array=arrow.array([datetime(2023, 10, 20)
+ days(0:1) NaT] , TimeUnit="Second"), ...
+ String="2023-10-20 00:00:00 | 2023-10-21 00:00:00 | null");
+ ArrayWithOneNull.Timestamp = timestampStruct;
+
+ stringStruct = struct(Array=arrow.array([missing "ABC" "ABCDE"]),
...
+ String="null | ""ABC"" | ""ABCDE""");
+ ArrayWithOneNull.String = stringStruct;
+ end
+
+ function ArrayWithMultipleNulls = initializeArrayWithMultipleNulls()
+ boolStruct = struct(Array=arrow.array([true false true], Valid=1),
...
+ String="true | null | null");
+ ArrayWithMultipleNulls.Boolean = boolStruct;
+
+ uint8Struct = struct(Array=arrow.array(uint8([1 10 100]),
Valid=2), ...
+ String="null | 10 | null");
+ ArrayWithMultipleNulls.UInt8 = uint8Struct;
+
+ int64Struct = struct(Array=arrow.array(int64([2 1 20]), Valid=3),
...
+ String="null | null | 20");
+ ArrayWithMultipleNulls.Int64 = int64Struct;
+
+ time64Struct = struct(Array=arrow.array(seconds([1 NaN NaN])), ...
+ String="00:00:01.000000 | null | null");
+ ArrayWithMultipleNulls.Time64 = time64Struct;
+
+ timestampStruct = struct(Array=arrow.array([datetime(2023, 10, 20)
NaT NaT] , TimeUnit="Second"), ...
+ String="2023-10-20 00:00:00 | null | null");
+ ArrayWithMultipleNulls.Timestamp = timestampStruct;
+
+ stringStruct = struct(Array=arrow.array([missing "ABC" missing]),
...
+ String="null | ""ABC"" | null");
+ ArrayWithMultipleNulls.String = stringStruct;
+ end
+ end
+
+ methods (Test)
+
+ function DisplayEmptyArray(testCase, EmptyArray)
+ import arrow.internal.test.display.makeLinkString
+
+ fullClassName = string(class(EmptyArray));
+ displayName = extractAfter(fullClassName, "arrow.array.");
+ classNameLink = makeLinkString(FullClassName=fullClassName, ...
+ ClassName=displayName, ...
+ BoldFont=true);
+ zeroString = getNumString(0);
+ header = compose(" %s with %s elements and %s null values" +
newline, ...
+ classNameLink, zeroString, zeroString);
+
+ expectedDisplay = char(header + newline);
+ actualDisplay = evalc('disp(EmptyArray)');
+ testCase.verifyEqual(actualDisplay, expectedDisplay);
+ end
+
+ function DisplayArrayWithOneElement(testCase, ArrayWithOneElement)
+ import arrow.internal.test.display.makeLinkString
+
+ array = ArrayWithOneElement.Array;
+ fullClassName = string(class(array));
+ displayName = extractAfter(fullClassName, "arrow.array.");
+ classNameLink = makeLinkString(FullClassName=fullClassName, ...
+ ClassName=displayName, ...
+ BoldFont=true);
+
+ numElementString = getNumString(1);
+ numNullString = getNumString(0);
+ header = compose(" %s with %s element and %s null values:" +
newline, ...
+ classNameLink, numElementString, numNullString);
+
+ body = " " + ArrayWithOneElement.String + newline + newline;
+ expectedDisplay = char(strjoin([header body], newline));
+ actualDisplay = evalc('disp(array)');
+ testCase.verifyEqual(actualDisplay, expectedDisplay);
+ end
+
+ function DisplayArrayWithMultipleElements(testCase,
ArrayWithMultipleElements)
+ import arrow.internal.test.display.makeLinkString
+
+ array = ArrayWithMultipleElements.Array;
+ fullClassName = string(class(array));
+ displayName = extractAfter(fullClassName, "arrow.array.");
+ classNameLink = makeLinkString(FullClassName=fullClassName, ...
+ ClassName=displayName, ...
+ BoldFont=true);
+
+ numElementString = getNumString(3);
+ numNullString = getNumString(0);
+ header = compose(" %s with %s elements and %s null values:" +
newline, ...
+ classNameLink, numElementString, numNullString);
+
+ body = " " + ArrayWithMultipleElements.String + newline +
newline;
+ expectedDisplay = char(strjoin([header body], newline));
+ actualDisplay = evalc('disp(array)');
+ testCase.verifyEqual(actualDisplay, expectedDisplay);
+ end
+
+ function DisplayArrayWithOneNull(testCase, ArrayWithOneNull)
+ import arrow.internal.test.display.makeLinkString
+
+ array = ArrayWithOneNull.Array;
+ fullClassName = string(class(array));
+ displayName = extractAfter(fullClassName, "arrow.array.");
+ classNameLink = makeLinkString(FullClassName=fullClassName, ...
+ ClassName=displayName, ...
+ BoldFont=true);
+
+ numElementString = getNumString(3);
+ numNullString = getNumString(1);
+ header = compose(" %s with %s elements and %s null value:" +
newline, ...
+ classNameLink, numElementString, numNullString);
+
+ body = " " + ArrayWithOneNull.String + newline + newline;
+ expectedDisplay = char(strjoin([header body], newline));
+ actualDisplay = evalc('disp(array)');
+ testCase.verifyEqual(actualDisplay, expectedDisplay);
+ end
+
+ function DisplayArrayWithMultipleNulls(testCase,
ArrayWithMultipleNulls)
+ import arrow.internal.test.display.makeLinkString
+
+ array = ArrayWithMultipleNulls.Array;
+ fullClassName = string(class(array));
+ displayName = extractAfter(fullClassName, "arrow.array.");
+ classNameLink = makeLinkString(FullClassName=fullClassName, ...
+ ClassName=displayName, ...
+ BoldFont=true);
+
+ numElementString = getNumString(3);
+ numNullString = getNumString(2);
+ header = compose(" %s with %s elements and %s null values:" +
newline, ...
+ classNameLink, numElementString, numNullString);
+
+
+ body = " " + ArrayWithMultipleNulls.String + newline + newline;
+ expectedDisplay = char(strjoin([header body], newline));
+ actualDisplay = evalc('disp(array)');
+ testCase.verifyEqual(actualDisplay, expectedDisplay);
+ end
+
+ function DisplayWindowSizeThree(testCase)
+ import arrow.internal.test.display.makeLinkString
+ array = arrow.array(1:8);
+ fullClassName = string(class(array));
+ displayName = extractAfter(fullClassName, "arrow.array.");
+ classNameLink = makeLinkString(FullClassName=fullClassName, ...
+ ClassName=displayName, ...
+ BoldFont=true);
+
+ numElementString = getNumString(8);
+ numNullString = getNumString(0);
+ header = compose(" %s with %s elements and %s null values:" +
newline, ...
+ classNameLink, numElementString, numNullString);
+
+ body = " 1 | 2 | 3 | ... | 6 | 7 | 8" + newline + newline;
+ expectedDisplay = char(strjoin([header body], newline));
+ actualDisplay = evalc('disp(array)');
+ testCase.verifyEqual(actualDisplay, expectedDisplay);
+
+ end
+
+ function DisplayStructArray(testCase)
+ import arrow.internal.test.display.makeLinkString
+
+ t = table((1:2)', ["A"; "B"], VariableNames=["Number", "Text"]);
+ structArray = arrow.array(t); %#ok<NASGU>
+
+ classNameLink =
makeLinkString(FullClassName="arrow.array.StructArray", ...
+ ClassName="StructArray", ...
+ BoldFont=true);
+
+ numElementString = getNumString(2);
+ numNullString = getNumString(0);
+ header = compose(" %s with %s elements and %s null values:" +
newline, ...
+ classNameLink, numElementString, numNullString);
+
+ body = " -- is_valid: all not null" + newline + ...
+ " -- child 0 type: double" + newline + ...
+ " [" + newline + ...
+ " 1," + newline + ...
+ " 2" + newline + ...
+ " ]" + newline + ...
+ " -- child 1 type: string" + newline + ...
+ " [" + newline + ...
+ " ""A""," + newline + ...
+ " ""B""" + newline + ...
+ " ]" + newline + newline;
+ expectedDisplay = char(strjoin([header body], newline));
+ actualDisplay = evalc('disp(structArray)');
+ testCase.verifyEqual(actualDisplay, expectedDisplay);
+ end
+
+ function DisplayListArray(testCase)
+ import arrow.internal.test.display.makeLinkString
+
+ Offsets = arrow.array(int32([0, 2, 3]));
+ Values = arrow.array([1, 2, 3]);
+
+ listArray = arrow.array.ListArray.fromArrays(Offsets, Values);
%#ok<NASGU>
+
+ classNameLink =
makeLinkString(FullClassName="arrow.array.ListArray", ...
+ ClassName="ListArray", ...
+ BoldFont=true);
+
+ numElementString = getNumString(2);
+ numNullString = getNumString(0);
+ header = compose(" %s with %s elements and %s null values:" +
newline, ...
+ classNameLink, numElementString, numNullString);
+
+ body = " [" + newline + ...
+ " [" + newline + ...
+ " 1," + newline + ...
+ " 2" + newline + ...
+ " ]," + newline + ...
+ " [" + newline + ...
+ " 3" + newline + ...
+ " ]" + newline + ...
+ " ]" + newline + newline;
+
+ expectedDisplay = char(strjoin([header body], newline));
+ actualDisplay = evalc('disp(listArray)');
+ testCase.verifyEqual(actualDisplay, expectedDisplay);
+ end
+
+ end
+
+end
+
+function numString = getNumString(num)
+ if usejava("desktop")
+ numString = compose("<strong>%d</strong>", num);
+ else
+ numString = compose("%d", num);
+ end
+ end
\ No newline at end of file