Skip to content

Commit

Permalink
Add Tables.isrowtable for convenience. Fixes #134 (#140)
Browse files Browse the repository at this point in the history
* Add Tables.isrowtable for convenience. Fixes #134

* Add section in docs about Tables.isrowtable

* Define: isrowtable(::Type{<:RowTable}) = true (#141)

* Define: isrowtable(::Type{<:RowTable}) = true

* Define: isrowtable(::Type{<:MatrixTable}) = true

* Define: isrowtable(::Type{<:RowIterator}) = true

Co-authored-by: Takafumi Arakaki <[email protected]>
  • Loading branch information
quinnj and tkf authored Feb 9, 2020
1 parent 46c483b commit 77cdd04
Show file tree
Hide file tree
Showing 6 changed files with 56 additions and 13 deletions.
8 changes: 8 additions & 0 deletions docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -358,3 +358,11 @@ And that's it. Our `MatrixTable` type is now a fully fledged, valid Tables.jl so
the ecosystem. Now, this is obviously not a lot of code; but then again, the actual Tables.jl interface
implementations tend to be fairly simple, given the other behaviors that are already defined for table types
(i.e. table types tend to already have a `getcolumn` like function defined).
### `Tables.isrowtable`
One option for certain table types is to define `Tables.isrowtable` to automatically satisfy the Tables.jl interface.
This can be convenient for "natural" table types that have row elements, along with other required properties.
```@docs
Tables.isrowtable
```
26 changes: 23 additions & 3 deletions src/Tables.jl
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,27 @@ columnaccess(::Type{<:AbstractColumns}) = true
columns(x::AbstractColumns) = x
schema(x::AbstractColumns) = nothing

# default definitions
"""
Tables.isrowtable(x) => Bool
For convenience, some table objects that are naturally "row oriented" can
define `Tables.isrowtable(::Type{TableType}) = true` to simplify satisfying the
Tables.jl interface. Requirements for defining `isrowtable` include:
* `Tables.rows(x) === x`, i.e. the table object itself is a `Row` iterator
* If the table object is mutable, it should support:
* `push!(x, row)`: allow pushing a single row onto table
* `append!(x, rows)`: allow appending set of rows onto table
* If table object is mutable and indexable, it should support:
* `x[i] = row`: allow replacing of a row with another row by index
A table object that defines `Tables.isrowtable` will have definitions for
`Tables.istable`, `Tables.rowaccess`, and `Tables.rows` automatically defined.
"""
function isrowtable end

isrowtable(::T) where {T} = isrowtable(T)
isrowtable(::Type{T}) where {T} = false

"""
Tables.istable(x) => Bool
Expand All @@ -175,7 +195,7 @@ of knowing that the generator is a table.
function istable end

istable(x::T) where {T} = istable(T) || TableTraits.isiterabletable(x) === true
istable(::Type{T}) where {T} = false
istable(::Type{T}) where {T} = isrowtable(T)

"""
Tables.rowaccess(x) => Bool
Expand All @@ -194,7 +214,7 @@ natural for them to *consume* instead of worrying about what and how the input p
function rowaccess end

rowaccess(x::T) where {T} = rowaccess(T)
rowaccess(::Type{T}) where {T} = false
rowaccess(::Type{T}) where {T} = isrowtable(T)

"""
Tables.columnaccess(x) => Bool
Expand Down
5 changes: 2 additions & 3 deletions src/fallbacks.jl
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,7 @@ end

Base.eltype(x::RowIterator{T}) where {T} = ColumnsRow{T}
Base.length(x::RowIterator) = x.len
istable(::Type{<:RowIterator}) = true
rowaccess(::Type{<:RowIterator}) = true
rows(x::RowIterator) = x
isrowtable(::Type{<:RowIterator}) = true

columnaccess(::Type{<:RowIterator}) = true
columns(x::RowIterator) = x.columns
Expand All @@ -77,6 +75,7 @@ end

# this is our generic Tables.rows fallback definition
function rows(x::T) where {T}
isrowtable(x) && return x
# because this method is being called, we know `x` didn't define it's own Tables.rows
# first check if it supports column access, and if so, wrap it in a RowIterator
if columnaccess(T)
Expand Down
4 changes: 1 addition & 3 deletions src/matrix.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ struct MatrixTable{T <: AbstractMatrix} <: AbstractColumns
matrix::T
end

istable(::Type{<:MatrixTable}) = true
isrowtable(::Type{<:MatrixTable}) = true
names(m::MatrixTable) = getfield(m, :names)

# row interface
Expand All @@ -26,9 +26,7 @@ getcolumn(m::MatrixRow, nm::Symbol) =
getfield(getfield(m, :source), :matrix)[getfield(m, :row), getfield(getfield(m, :source), :lookup)[nm]]
columnnames(m::MatrixRow) = names(getfield(m, :source))

rowaccess(::Type{<:MatrixTable}) = true
schema(m::MatrixTable{T}) where {T} = Schema(Tuple(names(m)), NTuple{size(getfield(m, :matrix), 2), eltype(T)})
rows(m::MatrixTable) = m
Base.eltype(m::MatrixTable{T}) where {T} = MatrixRow{T}
Base.length(m::MatrixTable) = size(getfield(m, :matrix), 1)

Expand Down
5 changes: 1 addition & 4 deletions src/namedtuples.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@
const RowTable{T} = AbstractVector{T} where {T <: NamedTuple}

# interface implementation
istable(::Type{<:RowTable}) = true
rowaccess(::Type{<:RowTable}) = true
# an AbstractVector of NamedTuple iterates `Row`s itself
rows(x::RowTable) = x
isrowtable(::Type{<:RowTable}) = true
schema(x::AbstractVector{NamedTuple{names, types}}) where {names, types} = Schema(names, types)
materializer(x::RowTable) = rowtable

Expand Down
21 changes: 21 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -741,3 +741,24 @@ Tables.columnnames(r::Columns) = fieldnames(Columns)
@test Tables.schema(col) === nothing
@test isequal(Tables.columntable(col), ct)
end

struct IsRowTable
rows::Vector{NamedTuple}
end

Base.iterate(x::IsRowTable) = iterate(x.rows)
Base.iterate(x::IsRowTable, st) = iterate(x.rows, st)
Base.length(x::IsRowTable) = length(x.rows)

Tables.isrowtable(::Type{IsRowTable}) = true

@testset "Tables.isrowtable" begin

nt = (a=1, b=3.14, c="hey")
rt = IsRowTable([nt, nt, nt])
@test Tables.istable(rt)
@test Tables.rowaccess(rt)
@test Tables.rows(rt) === rt
@test Tables.columntable(rt) == Tables.columntable([nt, nt, nt])

end

0 comments on commit 77cdd04

Please sign in to comment.