Access a struct field from a string

Hey Everyone!

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.

Thanks for your help!

Luigi

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 mydict Symbols instead of strings to begin with.
5 Likes

In other words, foo.bar = "hello" is just a shorthand for:

setproperty!(foo, :bar, "hello")

which itself will just call the built-in function:

setfield!(foo, :bar, "hello")

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)]

1 Like

@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!

Thanks a lot

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.

3 Likes

Thank you very much!