# Automatic building of a vector containing modified instances of a structure

Dear all

I have the following structure MyPar (this is a reproductible fictive example):

``````Base.@kwdef struct MyPar
n1::Int = 1
ep::Float64 = .01
co::Float64 = 1.
dg::Int = 3
it::Bool = false
end
``````
``````julia> par = MyPar()   # Default instance
MyPar(1, 0.01, 1.0, 3, false)
``````
``````## Example of instance where some values are modified from the default
julia> par = MyPar(ep = 1.23, co = 12)
MyPar(1, 1.23, 12.0, 3, false)
``````

And now I have the following NamedTuple, say pars, containing vectors (of equal lengths) of values for some of the elements of MyPar (the elements in pars can vary, below is just an example, other elements than ep and dg can be present):

``````julia> pars = (ep = [10, -7., .8], dg = [1, 25, 4])
(ep = [10.0, -7.0, 0.8], dg = [1, 25, 4])
``````

What I would like is to automatically create a vector, say listpar, of length = length(pars) whose each element “i” will contains an instance of MyPar where the values are modified from the contents of pars, i.e. in the above example of pars:

``````## Building listpar by hand
## (this what I would like to automatize)
ncomb = length(pars)
listpar = Vector(undef, n)
i = 1 ; listpar[i] = MyPar(ep = pars.ep[i], dg = pars.dg[i])
i = 2 ; listpar[i] = MyPar(ep = pars.ep[i], dg = pars.dg[i])
i = 3 ; listpar[i] = MyPar(ep = pars.ep[i], dg = pars.dg[i])
``````
``````## Expected result
julia> listpar
3-element Vector{Any}:
MyPar(1, 10.0, 1.0, 1, false)
MyPar(1, -7.0, 1.0, 25, false)
MyPar(1, 0.8, 1.0, 4, false)
``````

Do you see a way to do this automatically, given MyPar and an object pars?

I found a quasi-solution (below, probably not optimal) but I needed to use a mutable structure (MyParMut, below) and, what I did not want, to create a global variable (par_tmp, below):

``````Base.@kwdef mutable struct MyParMut
n1::Int = 1
ep::Float64 = .01
co::Float64 = 1.
dg::Int = 3
it::Bool = false
end
pars = (ep = [10, -7., .8], dg = [1, 25, 4])   ## A given pars
nampars = keys(pars)
npars = length(nampars)
ncomb = length(pars)
listpar = Vector(undef, ncomb)
for i = 1:ncomb
global par_tmp = MyParMut()
for j = 1:npars
nam = nampars[j]
val = pars[nam][i]
z = string("par_tmp.", nam, "=", val)
eval(Meta.parse(z))    ## Problem here since eval works in the global scope
end
listpar[i] = par_tmp
end
``````
``````julia> listpar
3-element Vector{Any}:
MyPar(1, 10.0, 1.0, 1, false)
MyPar(1, -7.0, 1.0, 25, false)
MyPar(1, 0.8, 1.0, 4, false)
``````

I hope my description was clear. Any idea to get an automatic way to do this (idealy from MyPar, but from MyParMut without the creation of a global variable would also be ok) would be appreciate.

``````julia> function my_pars(pars)
v = Vector{MyPar}(undef, 0)
for i in eachindex(pars)
push!(v, MyPar(ep = pars.ep[i], dg = pars.dg[i]))
end
return v
end
my_pars (generic function with 1 method)

julia> pars = (ep = [10, -7., .8], dg = [1, 25, 4])
(ep = [10.0, -7.0, 0.8], dg = [1, 25, 4])

julia> my_pars(pars)
3-element Vector{MyPar}:
MyPar(1, 10.0, 1.0, 1, false)
MyPar(1, -7.0, 1.0, 25, false)
MyPar(1, 0.8, 1.0, 4, false)

``````

Thanks but your fonction only works for ep and dg (this is not what I look for):

I think I did not emphasize enough the fact that pars can vary (I edited my first message), i.e. not always contain the ep or dg elements (but any of the ones belonging to MyPar), for instance it can be:

``````julia> pars = (ep = [10, -7.], co = [-1., .28])
(ep = [10.0, -7.0], co = [-1.0, 0.28])
``````

or

``````julia> pars = (n1 = collect(1:5),)
(n1 = [1, 2, 3, 4, 5],)
``````

or

``````julia> pars = (ep = [10, -7.], it = [true, true], co = [-1., .28])
(ep = [10.0, -7.0], it = Bool[1, 1], co = [-1.0, 0.28])
``````

etc.

This is the difficulty I try to solve

1 Like

With `MyPar` and `pars` as in the OP:

``````julia> [MyPar(;kws...) for
kws in zip([[k=>vv for vv in v] for (k,v) in pairs(pars)]...)]
3-element Vector{MyPar}:
MyPar(1, 10.0, 1.0, 1, false)
MyPar(1, -7.0, 1.0, 25, false)
MyPar(1, 0.8, 1.0, 4, false)
``````

This isn’t optimized in terms of allocations. But might be enough.

2 Likes

Clearly it is enough! Amazing! many thanks @Dan