Arrow. Logical
(Arrow v0.1.0)
Copy Markdown
Logical (null-aware) view of Arrow.Array.* columns.
Two Arrow arrays are logically equal if, for each slot, both are null or both carry the same value. Byte-level differences that don't affect that observation — trailing junk bits in a validity bitmap, arbitrary content at null positions in a value buffer, two equivalent encodings of the same logical value — should not register as a difference.
This module produces a canonical Elixir representation of each array:
to_list(%Arrow.Array.Int32{...}) #=> [1, nil, 3, nil, 5]
to_list(%Arrow.Array.Utf8{...}) #=> ["foo", nil, "bar"]
to_list(%Arrow.Array.List{...}) #=> [[1, 2], [], [3, nil, 5]]
to_list(%Arrow.Array.Struct{...}) #=> [[1, "a"], [2, nil], nil]Equality is then plain to_list(a) == to_list(b). equal?/2,
arrays_equal?/2, and batches_equal?/2 are shortcuts.
Caveats:
- Field names are not preserved for
Structslots — children are represented positionally so two equivalent arrays compare equal without needing the schema. Useto_list/1plus the schema if you want field-named output. Float32/Float64NaNslots do not compare equal to themselves, becausenil == nilbutNaN != NaNper IEEE-754. Callers comparing float data with NaNs should custom-compare.Decimal128values are returned as their unscaled integer. Scale/precision are properties of the type, not the value.
Summary
Types
Dictionary registry: id → dictionary values array.
Functions
True iff two arrays are logically equal, given their dictionary registries.
True iff two record batches are logically equal.
True iff every dictionary-encoded field's dictionary is resolvable in both registries, given the schemas referring to them.
Registry-less logical equality. Arrow.RecordBatch inputs dispatch
to batches_equal?/2; any other pair of array structs dispatches to
arrays_equal?/2. Neither path takes dictionary registries, so
inputs containing Arrow.Array.Dictionary columns raise — use
arrays_equal?/4 or batches_equal?/4 with registries instead.
True iff a complete payload (schema + dictionaries + batches) is logically equivalent to another. Dictionary encoding details are not compared directly: ID assignment, dictionary entry order, and unreferenced dictionary entries may all differ between the two sides, provided the resolved per-slot values agree.
True iff two schemas describe the same logical shape.
Converts an Arrow.Array.* into a list of native Elixir values, with
nil for null slots. Pass a dictionaries registry to resolve
Arrow.Array.Dictionary columns to their dictionary values.
Types
@type dictionaries() :: %{optional(non_neg_integer()) => Arrow.Array.t()}
Dictionary registry: id → dictionary values array.
Functions
@spec arrays_equal?(Arrow.Array.t(), Arrow.Array.t(), dictionaries(), dictionaries()) :: boolean()
True iff two arrays are logically equal, given their dictionary registries.
@spec batches_equal?( Arrow.RecordBatch.t(), Arrow.RecordBatch.t(), dictionaries(), dictionaries() ) :: boolean()
True iff two record batches are logically equal.
Schemas are compared via schemas_equivalent?/2, which treats
dictionary IDs as opaque labels — producers may assign different IDs
to semantically-identical dictionaries. The optional registries are
used to resolve Arrow.Array.Dictionary columns to their values
during comparison.
@spec dictionaries_equivalent_via_schemas?( Arrow.Schema.t(), dictionaries(), Arrow.Schema.t(), dictionaries() ) :: boolean()
True iff every dictionary-encoded field's dictionary is resolvable in both registries, given the schemas referring to them.
Walks the field tree of both schemas in parallel; for every
dict-encoded field pair, checks that the corresponding dictionary is
present in each registry under the field's ID. ID values themselves
are never compared directly, and the dictionary arrays are not
compared element-wise — two payloads carrying permuted or superset
dictionaries with correspondingly adjusted indices encode the same
logical values, so the value-level comparison is left to
batches_equal?/4, which resolves indices through the registries.
This mirrors the official Arrow integration comparison, which
compares resolved values only.
Registry-less logical equality. Arrow.RecordBatch inputs dispatch
to batches_equal?/2; any other pair of array structs dispatches to
arrays_equal?/2. Neither path takes dictionary registries, so
inputs containing Arrow.Array.Dictionary columns raise — use
arrays_equal?/4 or batches_equal?/4 with registries instead.
@spec payloads_equivalent?( %{ schema: Arrow.Schema.t(), dictionaries: dictionaries(), batches: [Arrow.RecordBatch.t()] }, %{ schema: Arrow.Schema.t(), dictionaries: dictionaries(), batches: [Arrow.RecordBatch.t()] } ) :: boolean()
True iff a complete payload (schema + dictionaries + batches) is logically equivalent to another. Dictionary encoding details are not compared directly: ID assignment, dictionary entry order, and unreferenced dictionary entries may all differ between the two sides, provided the resolved per-slot values agree.
@spec schemas_equivalent?(Arrow.Schema.t(), Arrow.Schema.t()) :: boolean()
True iff two schemas describe the same logical shape.
Identical to == except that:
Arrow.Field.dictionary.idis treated as an opaque label: two fields where one hasdictionary.id = 0and the other hasdictionary.id = 7are still equivalent provided the rest of their dictionary annotation (index_type, is_ordered) and surrounding field structure agrees.- The names of a Map field's children (the entries struct and its key/value fields) are ignored — per the spec they are recommendations, not part of the logical type.
Use == instead if you need strict structural equality.
@spec to_list(Arrow.Array.t(), dictionaries()) :: [term()]
Converts an Arrow.Array.* into a list of native Elixir values, with
nil for null slots. Pass a dictionaries registry to resolve
Arrow.Array.Dictionary columns to their dictionary values.