Filling an array of structs from file contents

Hi, I have code with a custom struct with different data types

mutable struct myStruct
    name::String = ""
    id::Int = -1
    ....
    # several more entries of varied types
    ....
end

I want to read a file and use several of these structs to capture the data in an accesible way. I do not know how many structs I will need at run-time. I want to read through the file, then add myStruct structs to a stored_array one at a time as I complete them. Something along the lines of

function add_element(stored_data::Array{myStruct}, new_data::myStruct)
    push!(stored_data, new_data)
    return stored_array
end

I have tried this, but I get an error that push! is not defined for these types. If I try to get define a new version of push!, my program does not use it. See these attempted definitions and error

# not all of these were used at the same time
stored_array = Array{myStruct}
stored_array = Array{myStruct, length}  # where length is an Int
stored_array = Array{myStruct}(undef, length)
function push!(stored_array::Type{Array{myStruct}}, data::myStruct)
    ....
end

function push!(stored_array::Array{myStruct}, data::myStruct)
    ...
end
ERROR: LoadError: MethodError: no method matching push!(::Type{Array{myStruct}}, ::myStruct)
Closest candidates are:
  push!(::Type{Vector{myStruct}}, ::myStruct) at C:\Users\qt\Desktop\Julia\code\Debug\decoder.jl:122
  push!(::Vector{myStruct}, ::myStruct) at C:\Users\qt\Desktop\Julia\code\Debug\decoder.jl:116

I feel that I am missing something much simpler. I have seen posts suggesting that looping over the array to initialize it is the best way to go, but with that I am having trouble initializing the array correctly.

Could someone who is more familiar with the language help me here? I’m open to changing my flow if there is a more “Julia” way to handle this use case.

I tried to reproduce add_element function, and it worked for me

mutable struct Foo
    name::String
    id::Int
end

add_element!(stored::Array{Foo}, x::Foo) = push!(stored, x)
add_element2!(stored::Vector{Foo}, x::Foo) = push!(stored, x)

A = [Foo("a", 1)]
add_element!(A, Foo("b", 2))
add_element2!(A, Foo("c", 3))
@show A  # A = Foo[Foo("a", 1), Foo("b", 2), Foo("c", 3)]

A = Foo[]
add_element!(A, Foo("a", 1))
@show A  # A = Foo[Foo("a", 1)]

About Julia-ways. If you just want to append a structure just use push! as it is common. Also, does myStruct is defined once you read a new line?

1 Like

This worked, and I can now just use push! instead of wrapping it in a function. The main difference was how you defined the array I’m pushing too.

I have a parsing function that fills the struct I push to the array that handles newlines and other variations. Thank you for the help on this!

Do you know why my versions of push! caused those errors? I tried to type them as arrays but the errors only mention vectors.

This error caused by pushing to type, not array (e.g. push!(Vector{Int}, 1)). Perhaps, this caused by invalid initialization of stored_array, see the difference

  • stored_array = Array{myStruct} — this is UnionAll type;
  • stored_array = Array{myStruct, length} — this is type too, it defines type of length-dimensional array of myStruct (see help for Array);
  • stored_array = Array{myStruct}(undef, length) — this is correct. It defines uninitialised array of dimension 1 with length elements.

In last case I recommend to use stored_array = Vector{myStruct}(undef, length), because it is clear that array dimension is one. Vector{T} is just alias for Array{T,1}, same for Matrix.

2 Likes