Silly question. How to add a row to array?

A 2d Array is inherently a bad data structure if you want to add rows dynamically. Julia arrays are stored column-major (contiguous columns), which means that even if you resized the memory to accommodate a new row, you would need to insert a new element into each column by moving every column in memory.

One possibility here is to use an vector of arrays, e.g.

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

julia> push!(a, [1,2])
3-element Array{Array{Int64,1},1}:
 [2, 3]
 [5, 6]
 [1, 2]

It would also be possible to define your own AbstractMatrix subtype that allowed you to append rows effiicently to contiguous memory, by implementing a 2d “view” into an underlying 1d array interpreted in row-major order, e.g.

mutable struct MyMatrix{T} <: AbstractMatrix{T}
    m::Int
    n::Int
    data::Vector{T}
end
Base.size(a::MyMatrix) = (a.m, a.n)
Base.getindex(a::MyMatrix, i::Integer, j::Integer) = a.data[(Int(i)-1)*a.n + Int(j)] # row-major
Base.setindex!(a::MyMatrix, v, i::Integer, j::Integer) = setindex!(a.data, v, (Int(i)-1)*a.n + Int(j))
MyMatrix{T}(::UndefInitializer, m::Integer, n::Integer) where {T} = MyMatrix{T}(m, n, Array{T}(undef, m*n))
MyMatrix(a::AbstractMatrix{T}) where {T} = copyto!(MyMatrix{T}(undef, size(a)...), a)

function Base.push!(a::MyMatrix, row::AbstractVector)
    a.n == length(row) || throw(DimensionMismatch("row size must match matrix"))
    resize!(a.data, length(a) + a.n)
    a.data[length(a)+1:length(a.data)] = row
    a.m += 1
    return a
end

at which point you can (fairly efficiently) do:

julia> a = MyMatrix([2 3; 5 6])
2×2 MyMatrix{Int64}:
 2  3
 5  6

julia> push!(a, [1,2])
3×2 MyMatrix{Int64}:
 2  3
 5  6
 1  2

(You will probably need to define more array methods for MyMatrix depending on what you want to do with it, however. The good news is that, assuming you know what you are doing, you can implement your own array type like this and make it just as fast and flexible as the “built in” Matrix type, but specialized to allow appended rows.)

The basic question here is what you want to do with the resulting array. That will help determine what data structure you want to use, but you haven’t explained anything about your application yet.

Yes, if you know the size of your data in advance, it is nearly always better to preallocate.

16 Likes