diff --git a/src/IndexedTables.jl b/src/IndexedTables.jl
index 51b081da..dfd5eecc 100644
--- a/src/IndexedTables.jl
+++ b/src/IndexedTables.jl
@@ -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}) =
diff --git a/test/test_core.jl b/test/test_core.jl
index 57ac6fd7..16c2f79c 100644
--- a/test/test_core.jl
+++ b/test/test_core.jl
@@ -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]