# Best way to query the values of a struct with array of symbols

I have an struct with some fields. Based on the types provided by the user at construction, I want the struct to remember certain fields of interest. I achieved this this way

``````mutable struct Foo{T}
a::T
b::T
c::Array{T,1}
fields::Array{Symbol,1}
end
``````

For now, let’s assume that the interesting fields are always a and c.

``````function Foo(a,b,c)
Foo(a,b,c,[:a,:c])
end

function get_interesting_fields(foo::Foo)
reduce(vcat, [getfield(foo, f) for f in foo.fields])
end
``````

What I want is to get a vector filled with a and the values in c. However this function is type unstable. Is there a better way to achieve this ?

I also would like to be able to change the value of the fields in the list. For example

``````function make_zeros(foo::Foo{T}) where T
for f in foo.fields
setfield!(foo, f, zero(T))
end
end
``````

`a` is a value of type `T`, `c` is a `Vector{T}`, a vector of values each of type `T`, you want to obtain a new vector `v = [a, c...]` that is
`v = [a, c[1], c[2], .. c[end]]`.

``````function cull_info(x)
return [x.a, x.c...]
end

myinfo = Foo(a,b,c)
v = cull_info(myinfo)
``````

Oh yes I shoudl have specified, I must somehow iterate foo.fields because it may not always contain `:a` and `:c`.

Do you always know the names of the fields of interest at the time you want to cull information from them, or do you always know the index into the field sequence (1 for the field that appears first in the struct …)? Or do you mean use `a` and `c` when present otherwise use either if present otherwise `nothing`?

At construction, Foo checks the type of the arguments `a`, if it’s a `Union{Real, Vector{<:Real}}, `:a` is pushed to the Symbol list. Then it does the same for b and c. So I guess I always know the names of the fields, but implicitly I can derive the index too I suppose.

Is this correct?

``````#=
a, b, and c are obtained
a, b, and/or c may or may not be a Real value or a vector of Real values
for each of a, b, c
if it is of a type T <: Union{Real, Vector{Real}}
then you want to save the field name as a symbol
and that is how the value of your field `fields` is developed
---
at some other processing
you receive (are passed, or otherwise obtain)
an previously valued realization of the struct type (an instance)
and from that you wish to cull, as a single vector,
all values that appear in the fields that are named as symbols in `fields`
in order of those named symbols
---
=#
``````

Yes

That we can do.
I am out for some hours now.
Either when I return, or before then (by another person).

``````
#=
TheStruct is the name of the structured type
thestruct = TheStruct(args...)
thestruct is the name of one realization of TheStruct

with each realization of `TheStruct`
the parameter `T1` becomes attached to a concrete type
this is the type of the first field of TheStruct
the parameter `T2` becomes attached to a concrete type
this is the type of the second field of TheStruct
=#

struct TheStruct{T1, T2}
a_field::T1
b_field::T2
fields_of_reals::Vector{Symbol}
end

const TheStruct_fieldnames = fieldnames(TheStruct)

#=
The default constructor for TheStruct requires three arguments,
which is clumsy.  We specify a dispatching constructor:
=#
function TheStruct(args...)
types_of_fields = typeof.(args)
itr = zip(TheStruct_fieldnames, types_of_fields)
real_fields = Symbol[]
for (name,T) in itr
if real_or_realvec(T)
push!(real_fields, name)
end
end
return TheStruct(args..., real_fields)
end

real_or_realvec(x) = eltype(x) <: Real

#= ~~~~~~~~~~~~ =#

a = 1618
b = [1.0, 2.0]
c = "c"

struct1 = TheStruct(a, b)
TheStruct{Int64,Array{Float64,1}}(1618, [1.0, 2.0], Symbol[:a_field, :b_field])

struct2 = TheStruct(a, c)
TheStruct{Int64,String}(1618, "c", Symbol[:a_field])

struct3 = TheStruct(c, a)
TheStruct{String,Int64}("c", 1618, Symbol[:b_field])

struct4 = TheStruct(c, c)
TheStruct{String,String}("c", "c", Symbol[])
``````
2 Likes

This is why I really love Julia. Thank you so much !