# Struggling to implement Tables.jl interface for Vector{MyStruct}

**URL:** <https://discourse.julialang.org/t/struggling-to-implement-tables-jl-interface-for-vector-mystruct/42318>\
**Category:** New to Julia\
**Tags:** data\_structures, parquet, tables\
**Created:** [June 30, 2020, 6:31pm UTC](https://discourse.julialang.org/t/struggling-to-implement-tables-jl-interface-for-vector-mystruct/42318 "2020-06-30T18:31:10Z")\
**Posts on this page:** 1\
**Showing post:** 7

<div class="post-metadata">

**Author:** ![quinnj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/quinnj/32/11_2.png) [@quinnj](https://discourse.julialang.org/u/quinnj)\
**Post date:** [July 2, 2020, 3:55pm UTC](https://discourse.julialang.org/t/struggling-to-implement-tables-jl-interface-for-vector-mystruct/42318/7 "2020-07-02T15:55:22Z")

</div>

Thanks for posting! Hopefully I can help clarify what’s (not) needed here. I can see why this isn’t super clear, but I’ll try to point out the relevant parts of the docs along the way:

> [@freeman](#):
>
> Now, according to the README.md of Tables.jl, [2] the only thing I should need is to define three functions:
> 
> Tables.istable(table) - Declare that your table type implements the interface  
> Tables.columnaccess(table) - Declare that your table type defines a Tables.columns(table) method  
> Tables.columns(table) - Return an Tables.AbstractColumns-compatible object from your table

One of the key design principles of the Tables.jl interface is that _providers_/_sources_ only **implement** what is natural, and _consumers_ only **call** what’s natural. So right from the get-go, your “table” is row-oriented, so you don’t need to think about providing column-access, only row-access.

The relevant interface functions for row-access are:

```julia
Tables.istable
Tables.rowaccess
Tables.rows

```

Fallback definitions for `Tables.istable` and `Tables.rowaccess` say that any iterable can be assumed to be a table and provide row-access, so check: your `Vector{MyStruct}` is iterable, so will be assumed to be a table and provide row-access. Relatedly, the default `Tables.rows` definition will basically return the input, though a check will be made that the iterator _does_ actually iterate “rows”.

So boom, your `Vector{MyStruct}` already satisfies the first three automatically via fallback definitions (if you didn’t want the validation check, you could define `Tables.rows(x::Vector{MyStruct}) = x`).

The 2nd part we need to satisfy is that our “row table” actually iterates “rows”; the relevant docs for this are [here](https://juliadata.github.io/Tables.jl/stable/#Tables.AbstractRow-1). Essentially, we need to define:

```julia
Tables.getcolumn(row, i::Int)
Tables.getcolumn(row, nm::Symbol)
Tables.columnnames(row)

```

But hey, let’s take a look at the default definitions:

```julia
Tables.getcolumn(row, i::Int) = getfield(row, i)
Tables.getcolumn(row, nm::Symbol) = getproperty(row, nm)
Tables.columnnames(row) = propertynames(row)

```

which happen to be exactly what you want for `MyStruct`! That is, `MyStruct` already satisfies the `AbstractRow` interface via the default definitions!

Wait, so `Vector{MyStruct}` is already a table?? By default?! Yes! Let’s see it in action:

```julia
julia> using DataFrames, Tables, Parquet
[Info: Precompiling Parquet [626c502c-15b0-58ad-a749-f091afb673ae]

julia> struct MyStruct                                                                 
               a::Float64                                                              
               b::Float64                                                              
       end

julia> t = [MyStruct(1, 2), MyStruct(3, 4)]
2-element Array{MyStruct,1}:
 MyStruct(1.0, 2.0)
 MyStruct(3.0, 4.0)

julia> DataFrame(t)
2×2 DataFrame
│ Row │ a │ b │
│ │ Float64 │ Float64 │
├─────┼─────────┼─────────┤
│ 1 │ 1.0 │ 2.0 │
│ 2 │ 3.0 │ 4.0 │

```

Boom! We can automatically transform `Vector{MyStruct}` into a `DataFrame`, and specifically _ **without** _ needing to define anything “column” related to `Vector{MyStruct}` (and coincidentally w/o defining _anything_, which is, in fact, by design 🙂 ). This works because as was noted, Tables.jl wants _providers_ to only need to implement what is _ **natural** _ for them, and not have to jump through weird hoops, or implement boiler plate code just so columns and rows can talk to each other. Tables.jl itself provides the most efficient “fallback” definitions for transforming rows =\> columns and vice versa. In this case specifically, Tables.jl defines a `Tables.buildcolumns` routine that will iterate row tables and “build up” column vectors that column consumers can use. What that means is that DataFrames.jl, _ **as a consumer** _, doesn’t need to do anything different for row table inputs vs. column table inputs. All it does is call `Tables.columns(x)` and it will get columns back, regardless of whether the input is row or column oriented.

Now, we still have the original question of how to get this all to work with Parquet.jl. Given our `Vector{MyStruct}` is already a table, it should just work, right?

```julia
julia> write_parquet("/home/myuser/lala.parquet", x)
ERROR: AssertionError: Tables.columnaccess(tbl)
Stacktrace:
 [1] write_parquet(::String, ::Array{MyStruct,1}; compression_codec::String) at /home/chronos/user/.julia/packages/Parquet/g6mqp/src/writer.jl:465
 [2] write_parquet(::String, ::Array{MyStruct,1}) at /home/chronos/user/.julia/packages/Parquet/g6mqp/src/writer.jl:465
 [3] top-level scope at REPL[76]:1

```

Oh shoot! It seems that Parquet.jl is being a bit too opinionated about the table inputs it accepts (code [here](https://github.com/JuliaIO/Parquet.jl/blob/6f445a8367aa28d722f5229ebeea8105a8ae8a82/src/writer.jl#L465)). What they _ **should** _ define is just `tbl = Tables.columns(x)` and it will “just work” for any column _ **or row** _ oriented input. Which I’ve proposed they do [here](https://github.com/JuliaIO/Parquet.jl/pull/96).

Hope all that helps!

---

_[View the full topic](https://discourse.julialang.org/t/struggling-to-implement-tables-jl-interface-for-vector-mystruct/42318)._
