Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

map(f, t): create Columns when f returns tuple / named tuple #54

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 78 additions & 1 deletion src/IndexedTables.jl
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,84 @@ end

# map and convert

map(f, x::IndexedTable) = IndexedTable(copy(x.index), map(f, x.data), presorted=true)
function _map_onerror!(i, val, f, out, inp)
n = length(inp)

T = promote_type(eltype(out), typeof(val))
if (T <: Tup) && (length(val) == nfields(T) && !any(t->t<:Vararg, T.parameters))
# There are just as many columns but the types of the
# elements has changed
cols = map(t->Array{t}(n), (T.parameters...))
promoted = Columns{T, typeof(cols)}(cols)
else
promoted = Array{T}(n)
end

# move existing data to new vector
rng = CartesianRange((1:(i-1),))
copy!(promoted, rng, out, rng)

# use current value
promoted[i] = val

# retry
_map!(f, promoted, inp, i+1)
end

function map(f, x::IndexedTable)
y1 = f(x.data[1])
if isa(y1, Tup)
data = Columns(map(x->[x], y1))
resize!(data, length(x.data))
y = (_map!(f, data, x.data, 2))
IndexedTable(copy(x.index), y, presorted=true)
else
IndexedTable(copy(x.index), map(f, x.data), presorted=true)
end
end

function _map!(f, data::AbstractArray, x, start)
@inbounds for i = start:length(x)
val = f(x[i])
try
data[i] = val
catch err
if isa(err, InexactError) ||
(isa(err, MethodError) && err.f === convert)

showerror(STDERR, err, catch_backtrace())
_map_onerror!(i, val, f, data, x)
else
rethrow(err)
end
end
end
data
end

function _map!(f, y::Columns, x::Columns, start)
@assert length(y) == length(x)
@inbounds for i = start:length(x)
v = f(x[i])
try
if nfields(v) == nfields(x.columns)
y[i] = v
else
return _map_onerror!(i, v, f, y, x)
end
catch err
if isa(err, InexactError) ||
(isa(err, MethodError) && err.f === convert)

showerror(STDERR, err, catch_backtrace())
_map_onerror!(i, v, f, y, x)
else
rethrow(err)
end
end
end
y
end

# lift projection on arrays of structs
map{T,D<:Tuple,C<:Tup,V<:Columns}(p::Proj, x::IndexedTable{T,D,C,V}) =
Expand Down
23 changes: 23 additions & 0 deletions test/test_core.jl
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,29 @@ let c = Columns([1,1,1,2,2], [1,2,4,3,5]),
@test summary(c) == "Columns{Tuple{Int64,Int64}}"
end

let
t = IndexedTable([1,2,3], Columns(x=[4,5,6]))
@test isa(map(x->x.x, t).data, Vector)
@test map(x->x.x, t).data == [4,5,6]

t1 = map(x->@NT(x=x.x,y=x.x^2), t)
@test isa(t1.data, Columns)
@test fieldnames(eltype(t1.data)) == [:x,:y]

t2 = map(x->(x.x,x.x^2), t)
@test isa(t2.data, Columns)
@test isa(t2.data.columns, Tuple{Vector{Int}, Vector{Int}})

t3 = map(x->ntuple(identity, x.x), t)
@test isa(t3.data, Vector)
@test eltype(t3.data) <: Tuple{Vararg{Int}}

y = [1, 1//2, "x"]
t4 = map(x->(x.x, y[x.x-3]), t)
@test isa(t4.data, Columns)
@test eltype(t4.data) <: Tuple{Int, Any}
end

let
t = IndexedTable([1], Columns([1]))
@test map(pick(1), t).data == [1]
Expand Down