Convert AbstractVector to Vector

What is a generic, idiomatic and performant method to convert an AbstractVector (which may have non-standard indexing) to a Vector?
I want to treat the AbstractVector as list of items to be “loaded” into a regular Vector.

MyConvert(A::AbstractArray{T}) where {T} = copy!(Vector{T}(undef, length(A)), A)

Will this always work, and be most efficient?

Easier to do Vector(A), for example:

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

But in general I would urge you to avoid this conversion if you can — a different array type could easily be more efficient than a Vector for various specialized cases. For example, a range like 1:10^7 requires vastly less memory than a corresponding Vector. It is better to write generic code that works with any array type. The compiler will specialize your code for you on the actual argument type that is passed.

2 Likes

I prefer A[:] or [A...] (for an arbitrary iterable object).

A[:] won’t convert to a Vector, if A is a UnitRange for example, and the argument splatting in [A...] will be very inefficient for larger sizes.

3 Likes

Vector(A) doesn’t seem to work for OffsetArrays:

Julia-1.2.0> using OffsetArrays

Julia-1.2.0> A = OffsetVector([1,2,3], -1:1)
3-element OffsetArray(::Array{Int64,1}, -1:1) with eltype Int64 with indices -1:1:
 1
 2
 3

Julia-1.2.0> Vector(A)
ERROR: BoundsError: attempt to access 3-element Array{Int64,1} at index [Base.IdentityUnitRange(-1:1)]
Stacktrace:
 [1] throw_boundserror(::Array{Int64,1}, ::Tuple{Base.IdentityUnitRange{UnitRange{Int64}}}) at .\abstractarray.jl:538
 [2] checkbounds at .\abstractarray.jl:503 [inlined]
 [3] copyto!(::Array{Int64,1}, ::OffsetArray{Int64,1,Array{Int64,1}}) at .\multidimensional.jl:902
 [4] Array{T,1} where T(::OffsetArray{Int64,1,Array{Int64,1}}) at .\array.jl:482
 [5] top-level scope at REPL[7]:1

whereas explicit copy to Vector does seem work:

Julia-1.2.0> copy!(Vector{Int}(undef, length(A)), A)
3-element Array{Int64,1}:
 1
 2
 3

Yes, this is a good point. I might have to rethink my implementation.

My usecase is for a “WeightedVector”, which returns values base on weights. In this case, it seemed appropriate for values to also be a Vector to match weights, so that the indices are the same (when weight[i] is selected, return value[i]).

struct WeightVector{W<:Real,V}
    weights::Vector{W}
    values::Vector{V}
    sumWeights::W

    function WeightVector{W,V}(weights::Vector{W}, values::Vector{V}) where {W<:Real,V}
        length(weights) == length(values) || throw(ArgumentError("length of weights and values vectors must be equal"))
        issorted(weights, rev=true) || throw(ArgumentError("weights must be sorted in descending order"))
        return new(weights, values, sum(weights))
    end
end

I could perhaps change values::Vector{V} ----> values::A where A<:AbstractVector

These two things are not related at all — you can have two AbstractVectors with the same indices.

Incidentally, I would just check

axes(weights, 1) == axes(values, 1)

to support generalized indexing.

Finally, you may be interested in

1 Like