Convert Vector{AbstractType} to Vector{ConcreteType}

How to Convert Vector{AbstractType} to Vector{ConcreteType} ?
e.g. Vector{Real} to Vector{Int}
and generate an error if the array contains different concrete types?

e.g. my below example:
I can only think of one way: First convert to Tuple, then collect back to Vector:
but this is really dirty, no?

 abstract type Operate end
 
 struct Cut <: Operate
     member::String
 end
 Cut() = Cut("t")
 
 struct Push <: Operate
     member::Int
 end
 Push() = Push(1)
 
 struct Roll <: Operate
     member::Symbol
 end
 Roll() = Roll(:a)
 
aa = Vector{Operate}(undef, 4)
aa[1] = Roll()
aa[2] = Roll()
aa[3] = Roll()
aa[4] = Roll()

bb = collect(Tuple(aa))
julia> abstract type Operate end

julia> struct Cut <: Operate
           member::String
       end

julia> Cut() = Cut("t")
Cut

julia> struct Push <: Operate
           member::Int
       end

julia> Push() = Push(1)
Push

julia> struct Roll <: Operate
           member::Symbol
       end

julia> Roll() = Roll(:a)
Roll

julia> aa = Vector{Operate}(undef, 4)
4-element Array{Operate,1}:
 #undef
 #undef
 #undef
 #undef

julia> aa[1] = Roll()
Roll(:a)

julia> aa[2] = Roll()
Roll(:a)

julia> aa[3] = Roll()
Roll(:a)

julia> aa[4] = Roll()
Roll(:a)

julia> [a for a ∈ aa]
4-element Array{Roll,1}:
 Roll(:a)
 Roll(:a)
 Roll(:a)
 Roll(:a)

If you want to throw an error if they aren’t all of one concrete type, a simple approach is

julia> function make_concrete(v)
           typeof(first(v))[vᵢ for vᵢ ∈ v]
       end
make_concrete (generic function with 1 method)

julia> make_concrete(aa)
4-element Array{Roll,1}:
 Roll(:a)
 Roll(:a)
 Roll(:a)
 Roll(:a)

julia> aa[3] = Cut()
Cut("t")

julia> make_concrete(aa)
ERROR: MethodError: Cannot `convert` an object of type Cut to an object of type Roll
Closest candidates are:
  convert(::Type{T}, ::T) where T at essentials.jl:170
  Roll(::Any) at REPL[6]:2
Stacktrace:
 [1] setindex!(::Array{Roll,1}, ::Cut, ::Int64)
 [2] make_concrete(::Array{Operate,1})
 [3] top-level scope at REPL[17]:1

Note that this wont error if you do define a convert method. If you want it to error anyway, I’d use a loop.

1 Like

You could just use map(identity, aa) or identity.(aa)

2 Likes

If you know the type that you want, you could just do Vector{Int}(array).

julia> a = Real[1,2,3]
3-element Array{Real,1}:
 1
 2
 3

julia> Vector{Int}(a)
3-element Array{Int64,1}:
 1
 2
 3

julia> Vector{Float64}(a)
3-element Array{Float64,1}:
 1.0
 2.0

Or, using @Mason’s suggestion:

julia> identity.(a)
3-element Array{Int64,1}:
 1
 2
 3

but this only works if the elements are all actually the same type:

julia> b = Real[1,2,3.0]
3-element Array{Real,1}:
 1  
 2  
 3.0

julia> identity.(b)
3-element Array{Real,1}:
 1  
 2  
 3.0

julia> Vector{Int}(b)
3-element Array{Int64,1}:
 1
 2
 3
1 Like