I have incurred in an issue that I believe is conceptual.
I have a struct that I want to fill from a dictionary and i’m encountering problems on accessing the struct fields. The main issue is that the dictionary is variable so I need the function to be quite flexible.
Here is what I was trying to do:
mydict = Dict{Any,Any}("name"=>"joe","color"=>"blue")
struct MyStruct
name::String
color::String
age::Float64
end
function myfunction(dictionary::Dict)
result = Array{MyStruct,1}(undef,1)
for i in keys(dictionary)
if Symbol(i) in fieldnames(MyStruct)
field_name = Symbol(i)
result[1].field_name = dictionary[i]
else
break
end
end
return result
end
my_result = myfunction(mydict)
basically the function first create a 1 element array of MyStruct, then check whether the keys of the dictionary are present in the struct fields. (Here a simplified version that breaks in case this is not true)
In case they are present the function should write in the struct the value of the dictionary for that key.
I’m getting an UndefRefError: access to undefined reference
I got what is the issue (Julia does not recognize field_name has field of MyStruct) But I haven’t find a solution for this.
The foo.bar syntax only works if “bar” is the actual name of the field, not a variable holding the name of the field. Instead, you want the setproperty! function, which takes the field name as an argument:
julia> mutable struct MyStruct
name::String
end
julia> m = MyStruct("hello")
MyStruct("hello")
julia> field = :name
:name
julia> setproperty!(m, field, "world")
"world"
julia> m
MyStruct("world")
A few things to note:
Your struct must be a mutable struct since you’re mutating it
The argument to setproperty! should be a Symbol, not a string. It’s easy to convert a string to a Symbol, e.g. setproperty!(m, Symbol("name"), "world") but you might be better off making the keys of mydictSymbols instead of strings to begin with.
Also, you cannot index the Array at the #undef element with result[1]
You could use a default value instead of undef like this: result = [MyStruct("Default","Default",0)]
@rdeits@AndiMD Thanks a lot! you both helped solving the question! Is there a Way I can put “two solutions”?
I solved it by calling setting the struct to mutable and bye initializing it as @AndiMD said. while in the function instead of result[1].field_name = dictionary[i] I put setproperty!(result[1],Symbol(i),dictionary[i]) as suggested by @rdeits
Can I ask if you could clarify the pros/cons of using mutable structs? I read the documentations but is still not 100% clear to me!
The advantages of a struct over a mutable struct are:
A struct can be cheaper to construct. This matters a lot if you are constantly creating new objects inside your loop.
A struct is immutable, which can help prevent subtle bugs when you accidentally change a field without meaning to. Many people find code that uses immutable objects to be easier to reason about.
Neither of these advantages are overwhelming, so feel free to pick whichever type suits your needs.