Skip to content

Commit f6fc9fe

Browse files
committed
Introduce ArrayTable based on Dictionaries.jl
1 parent 15a746a commit f6fc9fe

File tree

4 files changed

+172
-4
lines changed

4 files changed

+172
-4
lines changed

Project.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
authors = ["Andy Ferris <ferris.andy@gmail.com>"]
22
name = "TypedTables"
33
uuid = "9d95f2ec-7b3d-5a63-8d20-e2491e220bb9"
4-
version = "1.2.2"
4+
version = "1.3.0"
55

66
[deps]
7+
Dictionaries = "85a47980-9c8c-11e8-2b9f-f7ca1fa99fb4"
78
SplitApplyCombine = "03a91e81-4c3e-53e1-a0a4-9c0c8f19dd66"
89
Tables = "bd369af6-aec1-5ad0-b16a-f7cc5008161c"
910
Unicode = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5"
@@ -12,6 +13,7 @@ Unicode = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5"
1213
julia = "1"
1314
SplitApplyCombine = "1"
1415
Tables = "1"
16+
Dictionaries = "0.3.8"
1517

1618
[extras]
1719
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

src/ArrayTable.jl

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
mutable struct ArrayTable{N, C <: AbstractDictionary{Symbol, <:AbstractArray{<:Any, N}}, I} <: AbstractArray{NamedTuple, N}
2+
columns::C
3+
indices::I
4+
5+
# Inner constructor, to compare axes?
6+
@inline function ArrayTable{N, C, I}(columns::C, indices::I) where {N, C, I}
7+
@boundscheck check_indices_match(columns, indices)
8+
new(columns, indices)
9+
end
10+
end
11+
12+
function check_indices_match(columns, indices)
13+
foreach(pairs(columns)) do (name, column)
14+
if keys(column) !== indices
15+
# TODO the keys print in long form...
16+
throw(DimensionMismatch("Column $name has indices $(keys(column)), which does not match table indices $indices"))
17+
end
18+
end
19+
end
20+
21+
Tables.columns(t::ArrayTable) = getfield(t, :columns)
22+
_indices(t::ArrayTable) = getfield(t, :indices)
23+
24+
columnnames(t::ArrayTable) = keys(columns(t))
25+
26+
ArrayTable() = ArrayTable(Dictionary{Symbol, Vector}(), LinearIndices{1,Tuple{Base.OneTo(0)}})
27+
@propagate_inbounds function ArrayTable(cols::AbstractDictionary{Symbol, <:AbstractArray{<:Any, N}}) where N
28+
if isempty(cols)
29+
if N == 1
30+
inds = LinearIndices((0,))
31+
else
32+
inds = CartesianIndices(ntuple(_ -> 0, Val(N)))
33+
end
34+
else
35+
inds = keys(first(cols))
36+
end
37+
return ArrayTable{N, typeof(cols), typeof(inds)}(cols, inds)
38+
end
39+
40+
Base.IndexStyle(::ArrayTable{<:Any, <:Any, <:LinearIndices}) = Base.IndexLinear()
41+
Base.IndexStyle(::ArrayTable{<:Any, <:Any, <:CartesianIndices}) = Base.IndexCartesian()
42+
43+
Base.axes(t::ArrayTable) = axes(_indices(t))
44+
Base.keys(t::ArrayTable) = keys(_indices(t))
45+
Base.length(t::ArrayTable) = length(_indices(t))
46+
Base.size(t::ArrayTable) = length(_indices(t))
47+
48+
@propagate_inbounds Base.getproperty(t::ArrayTable, s::Symbol) = getindex(columns(t), s)
49+
50+
@inline function Base.getindex(t::ArrayTable{<:Any, C, <:LinearIndices}, i::Integer) where {C}
51+
@boundscheck checkbounds(_indices(t), i)
52+
return ArrayTableRow{Any, C, typeof(i)}(columns(t), i)
53+
end
54+
55+
@inline function Base.getindex(t::ArrayTable{<:Any, C, <:CartesianIndices}, i::Integer...) where {C}
56+
@boundscheck checkbounds(_indices(t), i)
57+
return ArrayTableRow{Any, C, typeof(i)}(columns(t), i)
58+
end
59+
60+
struct ArrayTableRow{T, C <: AbstractDictionary{Symbol, <:AbstractArray}, I} <: AbstractDictionary{Symbol, T}
61+
columns::C
62+
index::I
63+
end
64+
65+
_columns(r::ArrayTableRow) = getfield(r, :columns)
66+
_index(r::ArrayTableRow) = getfield(r, :index)
67+
68+
Dictionaries.keys(r::ArrayTableRow) = keys(_columns(r))
69+
70+
Dictionaries.isinsertable(::ArrayTableRow) = false
71+
Dictionaries.issettable(r) = true # Should depend on array type and can vary from column to column?
72+
73+
Dictionaries.isassigned(r::ArrayTableRow, s::Symbol) = isassigned(_columns(r), s)
74+
@propagate_inbounds function Dictionaries.getindex(r::ArrayTableRow, s::Symbol)
75+
c = _columns(r)[s]
76+
return @inbounds c[_index(r)]
77+
end
78+
@propagate_inbounds function Dictionaries.setindex!(r::ArrayTableRow{T}, value::T, s::Symbol) where {T}
79+
c = _columns(r)[s]
80+
return @inbounds c[_index(r)] = value
81+
end
82+
83+
Dictionaries.istokenizable(r::ArrayTableRow) = istokenizable(_columns(r))
84+
Dictionaries.gettoken(r::ArrayTableRow, s::Symbol) = gettoken(_columns(r), s)
85+
Dictionaries.istokenassigned(r::ArrayTableRow, token) = istokenassigned(_columns(r), token)
86+
Dictionaries.gettokenvalue(r::ArrayTableRow, token) = @inbounds gettokenvalue(_columns(r), token)[_index(r)]
87+
Dictionaries.settokenvalue!(r::ArrayTableRow{T}, token, value::T) where {T} = @inbounds gettokenvalue(_columns(r), token)[_index(r)] = value
88+
89+
# show
90+
91+
Base.show(io::IO, ::MIME"text/plain", t::ArrayTable) = showtable(io, t)
92+
Base.show(io::IO, t::ArrayTable) = showtable(io, t)
93+
94+
# Support Vector / deque interface (mutable-length vectors)
95+
96+
function Base.empty!(t::ArrayTable)
97+
map(empty!, columns(t))
98+
return t
99+
end
100+
101+
function Base.pop!(t::ArrayTable)
102+
return map(pop!, columns(t))
103+
end
104+
105+
function Base.push!(t::ArrayTable, v::AbstractDictionary)
106+
map(push!, columns(t), v)
107+
return t
108+
end
109+
110+
function Base.append!(t::ArrayTable, t2::AbstractVector)
111+
map(append!, columns(t), columns(t2))
112+
return t
113+
end
114+
115+
function Base.popfirst!(t::ArrayTable)
116+
return map(popfirst!, columns(t))
117+
end
118+
119+
function Base.pushfirst!(t::ArrayTable, v::AbstractDictionary)
120+
map(pushfirst!, columns(t), v)
121+
return t
122+
end
123+
124+
function Base.prepend!(t::ArrayTable, t2::AbstractVector)
125+
map(prepend!, columns(t), columns(t2))
126+
return t
127+
end
128+
129+
function Base.deleteat!(t::ArrayTable, i)
130+
map(col -> deleteat!(col, i), columns(t))
131+
return t
132+
end
133+
134+
function Base.insert!(t::ArrayTable, i::Integer, v::AbstractDictionary)
135+
map((col, val) -> insert!(col, i, val), columns(t), v)
136+
return t
137+
end
138+
139+
function Base.splice!(t::ArrayTable, inds::Integer)
140+
return map(col -> splice!(col, inds), columns(t))
141+
end
142+
143+
function Base.splice!(t::ArrayTable, inds::AbstractArray)
144+
cols = map(col -> splice!(col, inds), columns(t))
145+
return @inbounds ArrayTable(cols)
146+
end
147+
148+
function Base.splice!(t::ArrayTable, inds::Integer, ins::AbstractDictionary)
149+
return map((col, vals) -> splice!(col, inds, vals), columns(t), ins)
150+
end
151+
152+
function Base.splice!(t::ArrayTable, inds::AbstractArray, ins::AbstractDictionary)
153+
cols = map((col, vals) -> splice!(col, inds, vals), columns(t), ins)
154+
return @inbounds ArrayTable(cols)
155+
end
156+
157+
function Base.splice!(t::ArrayTable, inds::Integer, ins::AbstractVector)
158+
return map((col, vals) -> splice!(col, inds, vals), columns(t), columns(ins))
159+
end
160+
161+
function Base.splice!(t::ArrayTable, inds::AbstractArray, ins::AbstractVector)
162+
cols = map((col, vals) -> splice!(col, inds, vals), columns(t), columns(ins))
163+
return @inbounds ArrayTable(cols)
164+
end

src/TypedTables.jl

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@ module TypedTables
33
using Unicode
44
using Tables
55
using SplitApplyCombine
6+
using Dictionaries
67

78
using Base: @propagate_inbounds, @pure, OneTo, Fix2
89
import Tables.columns, Tables.rows
910

1011
export @Compute, @Select
11-
export Table, FlexTable, columns, rows, columnnames, showtable
12+
export Table, FlexTable, ArrayTable, columns, rows, columnnames, showtable
1213

1314
# Resultant element type of given column arrays
1415
@generated function _eltypes(a::NamedTuple{names, T}) where {names, T <: Tuple{Vararg{AbstractArray}}}
@@ -19,7 +20,7 @@ export Table, FlexTable, columns, rows, columnnames, showtable
1920
return NamedTuple{names, Tuple{Ts...}}
2021
end
2122

22-
_ndims(a::NamedTuple{<:Any, T}) where {T} = _ndims(T)
23+
_ndims(::NamedTuple{<:Any, T}) where {T} = _ndims(T)
2324
_ndims(::Type{<:Tuple{Vararg{AbstractArray{<:Any, n}}}}) where {n} = n
2425

2526
# The following code causes newer versions of Julia to hang in precompilation
@@ -42,6 +43,7 @@ end
4243
include("properties.jl")
4344
include("Table.jl")
4445
include("FlexTable.jl")
46+
include("ArrayTable.jl")
4547
include("columnops.jl")
4648
include("show.jl")
4749

src/show.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ end
9898

9999
function showtable(io::IO, @nospecialize t)
100100
row_inds = keys(t)
101-
col_inds = columnnames(t)
101+
col_inds = collect(columnnames(t))
102102
nrows = length(row_inds)::Int
103103
nrowstring = join(map(string, size(t)), "×")
104104
ncols = length(col_inds)::Int

0 commit comments

Comments
 (0)